From: Saloni Jain Date: Thu, 18 Feb 2016 10:24:26 +0000 (+0530) Subject: Implement OFPT_TABLE_STATUS Message. X-Git-Url: http://git.cascardo.info/?p=cascardo%2Fovs.git;a=commitdiff_plain;h=6c6eedc5d6730835a0d9724e2e8cfe9cdf03b07d Implement OFPT_TABLE_STATUS Message. On change in a table state, the controller needs to be informed with the OFPT_TABLE_STATUS message. The message is sent with reason OFPTR_VACANCY_DOWN or OFPTR_VACANCY_UP in case of change in remaining space eventually crossing any one of the threshold. Signed-off-by: Saloni Jain Co-authored-by: Rishi Bamba Signed-off-by: Rishi Bamba [blp@ovn.org added vacancy event initialization and tests and updated NEWS] Signed-off-by: Ben Pfaff --- diff --git a/NEWS b/NEWS index 57a250ef3..156fc07b1 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Post-v2.5.0 - OpenFlow: * OpenFlow 1.1+ OFPT_QUEUE_GET_CONFIG_REQUEST now supports OFPP_ANY. * OpenFlow 1.4+ OFPMP_QUEUE_DESC is now supported. + * OpenFlow 1.4+ OFPT_TABLE_STATUS is now supported. * New property-based packet-in message format NXT_PACKET_IN2 with support for arbitrary user-provided data and for serializing flow table traversal into a continuation for later resumption. diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h index 49a4f7b51..4599f9566 100644 --- a/include/openflow/openflow-1.4.h +++ b/include/openflow/openflow-1.4.h @@ -157,6 +157,14 @@ struct ofp14_table_desc { }; OFP_ASSERT(sizeof(struct ofp14_table_desc) == 8); +/* A table config has changed in the datapath */ +struct ofp14_table_status { + uint8_t reason; /* One of OFPTR_*. */ + uint8_t pad[7]; /* Pad to 64 bits */ + /* Followed by struct ofp14_table_desc */ +}; +OFP_ASSERT(sizeof(struct ofp14_table_status) == 8); + /* ## ---------------- ## */ /* ## ofp14_port_stats ## */ /* ## ---------------- ## */ diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h index 7b1d4a41b..22f286408 100644 --- a/lib/ofp-msgs.h +++ b/lib/ofp-msgs.h @@ -256,6 +256,9 @@ enum ofpraw { /* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */ OFPRAW_OFPT14_ROLE_STATUS, + /* OFPT 1.4+ (31): struct ofp14_table_status, uint8_t[8][]. */ + OFPRAW_OFPT14_TABLE_STATUS, + /* OFPT 1.4+ (32): struct ofp14_requestforward, uint8_t[8][]. */ OFPRAW_OFPT14_REQUESTFORWARD, @@ -586,6 +589,9 @@ enum ofptype { /* Request forwarding by the switch. */ OFPTYPE_REQUESTFORWARD, /* OFPRAW_OFPT14_REQUESTFORWARD. */ + /* Asynchronous messages. */ + OFPTYPE_TABLE_STATUS, /* OFPRAW_OFPT14_TABLE_STATUS. */ + OFPTYPE_BUNDLE_CONTROL, /* OFPRAW_OFPT14_BUNDLE_CONTROL. */ OFPTYPE_BUNDLE_ADD_MESSAGE, /* OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE. */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 14908bd44..271b095b7 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -1139,6 +1139,28 @@ ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td) ds_put_char(string, '\n'); } +static void +ofp_print_table_status_message(struct ds *string, const struct ofp_header *oh) +{ + struct ofputil_table_status ts; + enum ofperr error; + + error = ofputil_decode_table_status(oh, &ts); + if (error) { + ofp_print_error(string, error); + return; + } + + if (ts.reason == OFPTR_VACANCY_DOWN) { + ds_put_format(string, " reason=VACANCY_DOWN"); + } else if (ts.reason == OFPTR_VACANCY_UP) { + ds_put_format(string, " reason=VACANCY_UP"); + } + + ds_put_format(string, "\ntable_desc:-"); + ofp_print_table_desc(string, &ts.desc); +} + static void ofp_print_queue_get_config_request(struct ds *string, const struct ofp_header *oh) @@ -3234,6 +3256,10 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw, ofp_print_requestforward(string, oh); break; + case OFPTYPE_TABLE_STATUS: + ofp_print_table_status_message(string, oh); + break; + case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REQUEST: ofp_print_stats(string, oh); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 5bc9674ca..de7cf3cea 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -9645,6 +9645,7 @@ ofputil_is_bundlable(enum ofptype type) case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: + case OFPTYPE_TABLE_STATUS: case OFPTYPE_NXT_TLV_TABLE_REQUEST: case OFPTYPE_NXT_TLV_TABLE_REPLY: case OFPTYPE_NXT_RESUME: @@ -10207,3 +10208,89 @@ ofputil_async_cfg_default(enum ofp_version version) .slave[OAM_PORT_STATUS] = OFPPR_BITS, }; } + +static void +ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td, + struct ofpbuf *b, enum ofp_version version) +{ + struct ofp14_table_desc *otd; + struct ofp14_table_mod_prop_vacancy *otv; + size_t start_otd; + + start_otd = b->size; + ofpbuf_put_zeros(b, sizeof *otd); + + ofpprop_put_u32(b, OFPTMPT14_EVICTION, td->eviction_flags); + + otv = ofpbuf_put_zeros(b, sizeof *otv); + otv->type = htons(OFPTMPT14_VACANCY); + otv->length = htons(sizeof *otv); + otv->vacancy_down = td->table_vacancy.vacancy_down; + otv->vacancy_up = td->table_vacancy.vacancy_up; + otv->vacancy = td->table_vacancy.vacancy; + + otd = ofpbuf_at_assert(b, start_otd, sizeof *otd); + otd->length = htons(b->size - start_otd); + otd->table_id = td->table_id; + otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, + td->eviction, td->vacancy, + version); +} + +/* Converts the abstract form of a "table status" message in '*ts' into an + * OpenFlow message suitable for 'protocol', and returns that encoded form in + * a buffer owned by the caller. */ +struct ofpbuf * +ofputil_encode_table_status(const struct ofputil_table_status *ts, + enum ofputil_protocol protocol) +{ + enum ofp_version version; + struct ofpbuf *b; + + version = ofputil_protocol_to_ofp_version(protocol); + if (version >= OFP14_VERSION) { + enum ofpraw raw; + struct ofp14_table_status *ots; + + raw = OFPRAW_OFPT14_TABLE_STATUS; + b = ofpraw_alloc_xid(raw, version, htonl(0), 0); + ots = ofpbuf_put_zeros(b, sizeof *ots); + ots->reason = ts->reason; + ofputil_put_ofp14_table_desc(&ts->desc, b, version); + ofpmsg_update_length(b); + return b; + } else { + return NULL; + } +} + +/* Decodes the OpenFlow "table status" message in '*ots' into an abstract form + * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_table_status(const struct ofp_header *oh, + struct ofputil_table_status *ts) +{ + const struct ofp14_table_status *ots; + struct ofpbuf b; + enum ofperr error; + enum ofpraw raw; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + ots = ofpbuf_pull(&b, sizeof *ots); + + if (raw == OFPRAW_OFPT14_TABLE_STATUS) { + if (ots->reason != OFPTR_VACANCY_DOWN + && ots->reason != OFPTR_VACANCY_UP) { + return OFPERR_OFPBPC_BAD_VALUE; + } + ts->reason = ots->reason; + + error = ofputil_decode_table_desc(&b, &ts->desc, oh->version); + return error; + } else { + return OFPERR_OFPBRC_BAD_VERSION; + } + + return 0; +} diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 187be8cd3..31cbe2db5 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -1405,4 +1405,16 @@ enum ofperr ofputil_decode_requestforward(const struct ofp_header *, struct ofputil_requestforward *); void ofputil_destroy_requestforward(struct ofputil_requestforward *); +/* Abstract ofp14_table_status. */ +struct ofputil_table_status { + enum ofp14_table_reason reason; /* One of OFPTR_*. */ + struct ofputil_table_desc desc; /* New table config. */ +}; + +enum ofperr ofputil_decode_table_status(const struct ofp_header *oh, + struct ofputil_table_status *ts); + +struct ofpbuf * +ofputil_encode_table_status(const struct ofputil_table_status *ts, + enum ofputil_protocol protocol); #endif /* ofp-util.h */ diff --git a/lib/rconn.c b/lib/rconn.c index 8aed96dc6..5706c3f2d 100644 --- a/lib/rconn.c +++ b/lib/rconn.c @@ -1404,6 +1404,7 @@ is_admitted_msg(const struct ofpbuf *b) case OFPTYPE_ROLE_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: + case OFPTYPE_TABLE_STATUS: case OFPTYPE_SET_FLOW_FORMAT: case OFPTYPE_FLOW_MOD_TABLE_ID: case OFPTYPE_SET_PACKET_IN_FORMAT: diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index cdd7a695f..59a870c88 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -1648,6 +1648,37 @@ connmgr_send_flow_removed(struct connmgr *mgr, } } +/* Sends an OFPT_TABLE_STATUS message with 'reason' to appropriate controllers + * managed by 'mgr'. When the table state changes, the controller needs to be + * informed with the OFPT_TABLE_STATUS message. The reason values + * OFPTR_VACANCY_DOWN and OFPTR_VACANCY_UP identify a vacancy message. The + * vacancy events are generated when the remaining space in the flow table + * changes and crosses one of the vacancy thereshold specified by + * OFPT_TABLE_MOD. */ +void +connmgr_send_table_status(struct connmgr *mgr, + const struct ofputil_table_desc *td, + uint8_t reason) +{ + struct ofputil_table_status ts; + struct ofconn *ofconn; + + ts.reason = reason; + ts.desc = *td; + + LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { + if (ofconn_receives_async_msg(ofconn, OAM_TABLE_STATUS, reason)) { + struct ofpbuf *msg; + + msg = ofputil_encode_table_status(&ts, + ofconn_get_protocol(ofconn)); + if (msg) { + ofconn_send(ofconn, msg, NULL); + } + } + } +} + /* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as * necessary according to their individual configurations. */ void diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h index f2e0a5f10..741034a06 100644 --- a/ofproto/connmgr.h +++ b/ofproto/connmgr.h @@ -217,4 +217,7 @@ void ofmonitor_compose_refresh_updates(struct rule_collection *rules, struct ovs_list *msgs) OVS_REQUIRES(ofproto_mutex); +void connmgr_send_table_status(struct connmgr *, + const struct ofputil_table_desc *td, + uint8_t reason); #endif /* connmgr.h */ diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 180ed486b..2c6e4839f 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -256,8 +256,11 @@ struct oftable { #define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */ unsigned int eviction; - /* If true, vacancy events are enabled; otherwise they are disabled. */ - bool vacancy_enabled; + /* If zero, vacancy events are disabled. If nonzero, this is the type of + vacancy event that is enabled: either OFPTR_VACANCY_DOWN or + OFPTR_VACANCY_UP. Only one type of vacancy event can be enabled at a + time. */ + enum ofp14_table_reason vacancy_event; /* Non-zero values for vacancy_up and vacancy_down indicates that vacancy * is enabled by table-mod, else these values are set to zero when diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 5fa34936a..1335da675 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -3594,24 +3594,35 @@ handle_table_features_request(struct ofconn *ofconn, return 0; } +/* Returns the vacancy of 'oftable', a number that ranges from 0 (if the table + * is full) to 100 (if the table is empty). + * + * A table without a limit on flows is considered to be empty. */ +static uint8_t +oftable_vacancy(const struct oftable *t) +{ + return (!t->max_flows ? 100 + : t->n_flows >= t->max_flows ? 0 + : (t->max_flows - t->n_flows) * 100.0 / t->max_flows); +} + static void query_table_desc__(struct ofputil_table_desc *td, struct ofproto *ofproto, uint8_t table_id) { - unsigned int count = ofproto->tables[table_id].n_flows; - unsigned int max_flows = ofproto->tables[table_id].max_flows; + const struct oftable *t = &ofproto->tables[table_id]; td->table_id = table_id; - td->eviction = (ofproto->tables[table_id].eviction & EVICTION_OPENFLOW + td->eviction = (t->eviction & EVICTION_OPENFLOW ? OFPUTIL_TABLE_EVICTION_ON : OFPUTIL_TABLE_EVICTION_OFF); td->eviction_flags = OFPROTO_EVICTION_FLAGS; - td->vacancy = (ofproto->tables[table_id].vacancy_enabled + td->vacancy = (t->vacancy_event ? OFPUTIL_TABLE_VACANCY_ON : OFPUTIL_TABLE_VACANCY_OFF); - td->table_vacancy.vacancy_down = ofproto->tables[table_id].vacancy_down; - td->table_vacancy.vacancy_up = ofproto->tables[table_id].vacancy_up; - td->table_vacancy.vacancy = max_flows ? (count * 100) / max_flows : 0; + td->table_vacancy.vacancy_down = t->vacancy_down; + td->table_vacancy.vacancy_up = t->vacancy_up; + td->table_vacancy.vacancy = oftable_vacancy(t); } /* This function queries the database for dumping table-desc. */ @@ -3651,6 +3662,40 @@ handle_table_desc_request(struct ofconn *ofconn, return 0; } +/* This function determines and sends the vacancy event, based on the value + * of current vacancy and threshold vacancy. If the current vacancy is less + * than or equal to vacancy_down, vacancy up events must be enabled, and when + * the current vacancy is greater or equal to vacancy_up, vacancy down events + * must be enabled. */ +static void +send_table_status(struct ofproto *ofproto, uint8_t table_id) +{ + struct oftable *t = &ofproto->tables[table_id]; + if (!t->vacancy_event) { + return; + } + + uint8_t vacancy = oftable_vacancy(t); + enum ofp14_table_reason event; + if (vacancy < t->vacancy_down) { + event = OFPTR_VACANCY_DOWN; + } else if (vacancy > t->vacancy_up) { + event = OFPTR_VACANCY_UP; + } else { + return; + } + + if (event == t->vacancy_event) { + struct ofputil_table_desc td; + query_table_desc__(&td, ofproto, table_id); + connmgr_send_table_status(ofproto->connmgr, &td, event); + + t->vacancy_event = (event == OFPTR_VACANCY_DOWN + ? OFPTR_VACANCY_UP + : OFPTR_VACANCY_DOWN); + } +} + static void append_port_stat(struct ofport *port, struct ovs_list *replies) { @@ -4686,6 +4731,9 @@ add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0, req ? req->ofconn : NULL, req ? req->request->xid : 0, NULL); + + /* Send Vacancy Events for OF1.4+. */ + send_table_status(ofproto, new_rule->table_id); } send_buffered_packet(req, fm->buffer_id, new_rule); @@ -5081,6 +5129,10 @@ delete_flows_finish__(struct ofproto *ofproto, ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason, req ? req->ofconn : NULL, req ? req->request->xid : 0, NULL); + + /* Send Vacancy Event for OF1.4+. */ + send_table_status(ofproto, rule->table_id); + ofproto_rule_remove__(ofproto, rule); learned_cookies_dec(ofproto, rule_get_actions(rule), &dead_cookies); @@ -6740,11 +6792,16 @@ table_mod__(struct oftable *oftable, if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { ovs_mutex_lock(&ofproto_mutex); - oftable->vacancy_enabled = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON - ? OFPTC14_VACANCY_EVENTS - : 0); oftable->vacancy_down = tm->table_vacancy.vacancy_down; oftable->vacancy_up = tm->table_vacancy.vacancy_up; + if (tm->vacancy == OFPUTIL_TABLE_VACANCY_OFF) { + oftable->vacancy_event = 0; + } else if (!oftable->vacancy_event) { + uint8_t vacancy = oftable_vacancy(oftable); + oftable->vacancy_event = (vacancy < oftable->vacancy_up + ? OFPTR_VACANCY_UP + : OFPTR_VACANCY_DOWN); + } ovs_mutex_unlock(&ofproto_mutex); } } @@ -7328,6 +7385,7 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: + case OFPTYPE_TABLE_STATUS: case OFPTYPE_NXT_TLV_TABLE_REPLY: default: if (ofpmsg_is_stat_request(oh)) { diff --git a/tests/ofproto.at b/tests/ofproto.at index bede254f8..b2b32b564 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1999,7 +1999,7 @@ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d AT_CHECK([ovs-ofctl -O Openflow14 mod-table br0 0 vacancy:20,80]) # Check that the configuration was updated. mv expout orig-expout -sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=0%/' expout +sed -e '3s/vacancy=off/vacancy=on vacancy_down=20% vacancy_up=80% vacancy=100%/' expout AT_CHECK([ovs-ofctl -O OpenFlow14 dump-table-desc br0 | sed '/^$/d /^OFPST_TABLE_DESC/d'], [0], [expout]) OVS_VSWITCHD_STOP @@ -2907,7 +2907,7 @@ OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=delete table_id=0" fi - # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE + # OFPT_FLOW_REMOVED, OFPRR_GROUP_DELETE ovs-ofctl -O OpenFlow14 add-group br0 group_id=1234,type=all,bucket=output:10 ovs-ofctl -O OpenFlow14 add-flow br0 send_flow_rem,actions=group:1234 ovs-ofctl -O OpenFlow14 --strict del-groups br0 group_id=1234 @@ -2915,6 +2915,84 @@ OFPT_PORT_STATUS (OF1.4): MOD: ${INDEX}(test): addr:aa:55:aa:55:00:0x echo >>expout "OFPT_FLOW_REMOVED (OF1.4): reason=group_delete table_id=0" fi + # OFPT_TABLE_STATUS, OFPTR_VACANCY_UP + if test X"$1" = X"OFPTR_VACANCY_UP"; then shift; + ovs-vsctl -- --id=@t1 create Flow_Table flow-limit=10 -- set bridge br0 flow_tables:1=@t1 + + # Turn on vacancy events, then add flows until we're full. + # With initial vacancy of 100% and vacancy_up of 80%, so that + # vacancy >= vacancy_up, this enables VACANY_DOWN events, so + # we get a single such message when vacancy dips below 20%. + ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=6,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=7,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=8,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=9,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=10,actions=2 + echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_DOWN +table_desc:- + table 1: + eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME + vacancy=on vacancy_down=20% vacancy_up=80% vacancy=10%" + # Then delete flows until we're empty. Sending the + # VACANCY_DOWN message enabled VACANCY_UP events, so we get a + # single such message when vacancy rises above 80%. + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=1 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=2 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=3 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=4 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=5 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=6 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=7 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=8 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=9 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=10 + echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP +table_desc:- + table 1: + eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME + vacancy=on vacancy_down=20% vacancy_up=80% vacancy=90%" + + # Now approach vacancy from the other direction. First + # disable vacancy events. With initial vacancy of 70%, so + # that vacancy < vacancy_up, this enables VACANCY_UP events. + # That means that filling up the table generates no message, + # but deleting all the flows generates VACANCY_UP at the point + # vacancy rises above 80%. + ovs-ofctl -O OpenFlow14 mod-table br0 1 novacancy + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=1,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=2,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=3,actions=2 + ovs-ofctl -O OpenFlow14 mod-table br0 1 vacancy:20,80 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=4,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=5,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=6,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=7,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=8,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=9,actions=2 + ovs-ofctl -O OpenFlow14 add-flow br0 table=1,in_port=10,actions=2 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=1 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=2 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=3 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=4 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=5 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=6 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=7 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=8 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=9 + ovs-ofctl -O OpenFlow14 del-flows br0 table=1,in_port=10 + echo >>expout "OFPT_TABLE_STATUS (OF1.4): reason=VACANCY_UP +table_desc:- + table 1: + eviction=off eviction_flags=OTHER|IMPORTANCE|LIFETIME + vacancy=on vacancy_down=20% vacancy_up=80% vacancy=90%" + fi + AT_FAIL_IF([test X"$1" != X]) ovs-appctl -t ovs-ofctl ofctl/barrier @@ -2940,8 +3018,8 @@ ovs-appctl -t ovs-ofctl ofctl/send 051800180000000200000003000000000000000000000 check_async 3 OFPPR_ADD OFPPR_MODIFY OFPPR_DELETE # Use OF 1.4 OFPT_SET_ASYNC to enable a patchwork of asynchronous messages. -ovs-appctl -t ovs-ofctl ofctl/send 051c0038000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c0005000800000005 -check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE +ovs-appctl -t ovs-ofctl ofctl/send 051c0040000000020000000800000005000100080000000200020008000000020003000800000005000400080000001c00050008000000050008000800000018 +check_async 4 OFPR_INVALID_TTL OFPPR_DELETE OFPRR_DELETE OFPRR_GROUP_DELETE OFPTR_VACANCY_UP # Set controller ID 123. ovs-appctl -t ovs-ofctl ofctl/send 05040018000000030000232000000014000000000000007b