Signed-off-by: Shuangmin Zhang <shuangm...@vmware.com>
--- vtep/ovs-vtep vtep/vtep-ctl.c 2 files changed, 462 insertions(+), 3 deletions(-) diff --git a/vtep/ovs-vtep b/vtep/ovs-vtep index 46a5692..41b3a9b 100755 --- a/vtep/ovs-vtep +++ b/vtep/ovs-vtep @@ -43,8 +43,11 @@ ps_name = "" ps_type = "" Tunnel_Ip = "" Lswitches = {} +Lrouters = {} Bindings = {} +Switch_Bindings = {} ls_count = 0 +lr_count = 0 tun_id = 0 bfd_bridge = "vtep_bfd" bfd_ref = {} @@ -56,6 +59,7 @@ def call_prog(prog, args_list): output = "" else: output = output[0].strip() + return output def ovs_vsctl(args): @@ -74,6 +78,227 @@ def unixctl_exit(conn, unused_argv, unused_aux): conn.reply(None) +class Logical_Router(object): + def __init__(self, lr_name): + global lr_count + self.name = lr_name + #lr_count += 1 + #self.short_name = "vtep_lr" + str(lr_count) + self.short_name = "vtep_" + lr_name + vlog.info("creating lrouter %s (%s)" % (self.name, self.short_name)) + self.ports = {} + self.dst_ips = {} + self.lifs = {} + self.setup_lr() + + def __del__(self): + vlog.info("destroying lrouter %s" % self.name) + + def setup_lr(self): + if ps_type: + ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s" + % (self.short_name, self.short_name, ps_type)) + else: + ovs_vsctl("--may-exist add-br %s" % self.short_name) + + ovs_vsctl("br-set-external-id %s vtep_logical_router true" + % self.short_name) + ovs_vsctl("br-set-external-id %s logical_router_name %s" + % (self.short_name, self.name)) + + ovs_ofctl("del-flows %s" % self.short_name) + ovs_ofctl("add-flow %s priority=0,action=drop" % self.short_name) + + def update_flood(self): + flood_ports = self.ports.values() + + ovs_ofctl("add-flow %s table=1,priority=0,action=%s" + % (self.short_name, ",".join(flood_ports))) + + def run(self): + #vlog.info("start lrouter running") + self.update_lif_macs() + self.update_forwarding_macs() + + def update_lif_macs(self): + lif_pl_uuid = vtep_ctl("--columns=_uuid find Physical_Locator dst_ip=127.0.0.1").partition(":")[2].strip() + new_lifs = set() + + # update lif macs + for switch_binding in Switch_Bindings.keys(): + lr_name, lif = switch_binding.split("-", 1) + if lr_name != self.name: + continue + + ls_name = Switch_Bindings[switch_binding] + ls_uuid = get_logical_switch_uuid(ls_name) + lif_mac_column = vtep_ctl("--columns=MAC find Ucast_Macs_Remote logical_switch=%s locator=%s" + % (ls_uuid, lif_pl_uuid)) + + if not lif_mac_column: + continue + + lif_ip = lif.strip("\"").partition("(")[2].partition("/")[0] + lif_ip_mask = lif.strip("\"").partition("(")[2].strip(")") + lif_port_r = lif.strip("\"").partition("(")[0].strip() + "-r" + lif_mac = lif_mac_column.partition(":")[2].strip() + + new_lifs.add(lif) + if lif in self.lifs.keys(): + if self.lifs[lif] == lif_mac: + continue + else: + del self.lifs[lif] + ovs_ofctl("del-flow %s ip,nw_dst=%s" % (self.short_name, lif_ip_mask)) + + self.lifs[lif] = lif_mac + + # get ip's hex format + lif_ip_hex = getHexIp(lif_ip) + lif_mac_hex = "0x" + "".join(lif_mac.strip("\"").split(":")) + + # create ARP flow table + ovs_ofctl("add-flow %s table=1,dl_type=0x0806,nw_dst=%s,actions=" + "move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:%s," + "load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[]," + "move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[]," + "load:%s->NXM_NX_ARP_SHA[]," + "load:%s->NXM_OF_ARP_SPA[],in_port" + % (self.short_name, lif_ip, lif_mac, lif_mac_hex, lif_ip_hex)) + + # don't flood arp request from same subnet (except gateway) to other lif + ovs_ofctl("add-flow %s table=1,priority=1,in_port=%s,dl_type=0x0806,nw_dst=%s,action=drop" + % (self.short_name, self.ports[lif_port_r], lif_ip_mask)) + + ovs_ofctl("add-flow %s table=2,priority=1,dl_type=0x0800,nw_dst=%s,action=mod_dl_src=%s,dec_ttl,resubmit(,3)" + % (self.short_name, lif_ip_mask, lif_mac)) + + dead_lifs = set(self.lifs.keys()).difference(new_lifs) + for lif in dead_lifs: + del self.lifs[lif] + lif_ip_mask = lif.strip("\"").partition("(")[2].strip(")") + ovs_ofctl("del-flow %s ip,nw_dst=%s" % (self.short_name, lif_ip_mask)) + + def update_forwarding_macs(self): + # update lif macs + lif_pl_uuid = vtep_ctl("--columns=_uuid find Physical_Locator dst_ip=127.0.0.1").partition(":")[2].strip() + + new_dst_ips = set() + for switch_binding in Switch_Bindings.keys(): + lr_name, lif = switch_binding.split("-", 1) + if lr_name != self.name: + continue + + ls_name = Switch_Bindings[switch_binding] + ls_uuid = get_logical_switch_uuid(ls_name) + lif_port = lif.strip("\"").partition("(")[0].strip() + "-r" + + new_dst_ips = new_dst_ips | self.update_forward_macs_by_table(ls_uuid, lif_pl_uuid, "Ucast_Macs_Local", lif_port) + new_dst_ips = new_dst_ips | self.update_forward_macs_by_table(ls_uuid, lif_pl_uuid, "Ucast_Macs_Remote", lif_port) + + dead_ips = set(self.dst_ips.keys()).difference(new_dst_ips) + for ip in dead_ips: + ovs_ofctl("del-flows %s ip,nw_dst=%s" % (self.short_name, ip)) + ovs_ofctl("del-flows %s dl_dst=%s" % (self.short_name, self.dst_ips[ip])) + del self.dst_ips[ip] + + + def update_forward_macs_by_table(self, ls_uuid, lif_pl_uuid, table, lif_port): + columns = vtep_ctl("--columns=locator find %s logical_switch=%s" % (table, ls_uuid)).splitlines() + new_dst_ips = set() + + for column in columns: + if not column: + continue + + pl_uuid = column.partition(":")[2].strip() + if pl_uuid == lif_pl_uuid: + continue + + ipaddr_lines = vtep_ctl("--columns=ipaddr find %s logical_switch=%s locator=%s" % (table, ls_uuid, pl_uuid)).splitlines() + for ipaddr_line in ipaddr_lines: + ipaddr = ipaddr_line.partition(":")[2].strip() + if not ipaddr: + continue + + dst_ip = ipaddr.strip("\"").partition("(")[2].strip(")") + new_dst_ips.add(dst_ip) + + # get destination's mac address from table + column = vtep_ctl("--columns=MAC find %s logical_switch=%s ipaddr=%s" + % (table, ls_uuid, ipaddr)) + dst_mac = column.partition(":")[2].strip() + + if dst_ip in self.dst_ips.keys(): + if self.dst_ips[dst_ip] == dst_mac: + continue + else: + ovs_ofctl("del-flows %s ip,nw_dst=%s" % (self.short_name, dst_ip)) + ovs_ofctl("del-flows %s dl_dst=%s" % (self.short_name, self.dst_ips[dst_ip])) + del self.dst_ips[dst_ip] + + self.dst_ips[dst_ip] = dst_mac + ovs_ofctl("add-flow %s table=3,priority=1000,dl_type=0x0800,nw_dst=%s,action=mod_dl_dst:%s,dec_ttl,output:%s" + % (self.short_name, dst_ip, dst_mac, self.ports[lif_port])) + + return new_dst_ips + + # binding logical switch's LIF with local router which + def add_switch_binding(self, binding, ls): + vlog.info("adding switch binding %s" % binding) + + lr_name, lif = binding.split("-", 1) + lif_port = lif.strip("\"").partition("(")[0].strip() + lif_port_r = lif_port+"-r" + lif_port_s = lif_port+"-s" + + lif_ip = lif.strip("\"").partition("(")[2].partition("/")[0] + lif_ip_mask = lif.strip("\"").partition("(")[2].strip(")") + + #Create a patch port that connects the Logical Switch to the Logical Router + ovs_vsctl("add-port %s %s " + " -- set Interface %s type=patch options:peer=%s" + % (ls.short_name, lif_port_s, lif_port_s, lif_port_r)) + ovs_vsctl("add-port %s %s " + " -- set Interface %s type=patch options:peer=%s" + % (self.short_name, lif_port_r, lif_port_r, lif_port_s)) + + port_s_no = ovs_vsctl("get Interface %s ofport" % lif_port_s) + port_r_no = ovs_vsctl("get Interface %s ofport" % lif_port_r) + + # create flow to switch to arp table whenever it is arp packet + #ovs_ofctl("add-flow %s table=0,priority=1000,dl_type=0x0806,actions=resubmit(,1)" % self.short_name) + + # create flow to switch to routing table for other packets + ovs_ofctl("add-flow %s table=0,priority=1,actions=resubmit(,2)" % self.short_name) + + ls.add_lbinding(lif_port_s) + self.add_lbinding(lif_port_r) + + def add_lbinding(self, lbinding): + vlog.info("adding %s binding to %s" % (lbinding, self.name)) + port_no = ovs_vsctl("get Interface %s ofport" % lbinding) + self.ports[lbinding] = port_no + ovs_ofctl("add-flow %s dl_type=0x0806,action=learn(table=1," + # "priority=1000,idle_timeout=15,cookie=0x5000," + "priority=1000,cookie=0x5000," + "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," + "output:NXM_OF_IN_PORT[]),resubmit(,1)" + % (self.short_name)) + + self.update_flood() + + def del_lbinding(self, lbinding, lif): + vlog.info("removing %s binding from %s" % (lbinding, self.name)) + port_no = self.ports[lbinding] + ovs_ofctl("del-flows %s in_port=%s" % (self.short_name, port_no)); + del self.ports[lbinding] + self.update_flood() + + lif_ip_mask = lif.strip("\"").partition("(")[2].strip(")") + ovs_ofctl("del-flows %s ip,nw_dst=%s" % (self.short_name, lif_ip_mask)); + ovs_ofctl("del-flows %s arp,arp_tpa=%s" % (self.short_name, lif_ip_mask)); + class Logical_Switch(object): def __init__(self, ls_name): global ls_count @@ -87,6 +312,7 @@ class Logical_Switch(object): self.remote_macs = {} self.unknown_dsts = set() self.tunnel_key = 0 + self.logical_router = None self.setup_ls() def __del__(self): @@ -115,7 +341,9 @@ class Logical_Switch(object): ovs_vsctl("br-set-external-id %s logical_switch_name %s" % (self.short_name, self.name)) - vtep_ctl("clear-local-macs %s" % self.name) + # since so far L3 local/remote macs are persisted in DB instead of querying by arp request, so keep from clean for now. + # will remove this limiation when arp resolution issue is solved + #vtep_ctl("clear-local-macs %s" % self.name) vtep_ctl("add-mcast-local %s unknown-dst %s" % (self.name, Tunnel_Ip)) ovs_ofctl("del-flows %s" % self.short_name) @@ -260,6 +488,9 @@ class Logical_Switch(object): continue if parse_ucast: + # exclude gateway's tunnel which has no meaning + if entry[2].find("127.0.0.1") != -1: + continue remote_macs[entry[1]] = entry[2] else: if entry[1] != "unknown-dst": @@ -475,6 +706,16 @@ def run_bfd(): bfd_lconf_default['bfd_config_local:bfd_dst_mac'], bfd_dst_mac)) +def getHexIp(ip): + hexip ="0x" + for i in ip.split('.'): + if int(i) < 16: + hexip += "0"+ hex(int(i))[2:] + else: + hexip += hex(int(i))[2:] + + return hexip + def add_binding(binding, ls): vlog.info("adding binding %s" % binding) @@ -516,6 +757,23 @@ def add_binding(binding, ls): ls.add_lbinding(lbinding) Bindings[binding] = ls.name +def del_switch_binding(binding, ls): + vlog.info("removing switch binding %s" % binding) + + lr_name, lif = binding.split("-", 1) + lif_port_r = lif.strip("\"").partition("(")[0].strip() + "-r" + lif_port_s = lif.strip("\"").partition("(")[0].strip() + "-s" + + lr = Lrouters[lr_name] + lr.del_lbinding(lif_port_r, lif) + ls.del_lbinding(lif_port_s) + + # Destroy the patch port that connects the lrouter to the lswitch + ovs_vsctl("del-port %s %s -- del-port %s %s" + % (lr.short_name, lif_port_r, ls.short_name, lif_port_s)) + + del Switch_Bindings[binding] + def del_binding(binding, ls): vlog.info("removing binding %s" % binding) @@ -545,6 +803,38 @@ def del_binding(binding, ls): del Bindings[binding] +def get_logical_switch_uuid(ls_name): + column = vtep_ctl("--columns=_uuid find Logical_Switch " + "name=%s" % ls_name) + return column.partition(":")[2].strip() + +def get_switch_bindings(): + # get the total list of switch bindings from all routers + New_Switch_Bindings = {} + binding_lines = set(vtep_ctl("--columns=switch_binding find Logical_Router").splitlines()) + for line in binding_lines: + binding_line = line.partition(":")[2].strip().strip("{}") + if not binding_line: + continue + + bindings = binding_line.split(",") + + # get lr name + column = vtep_ctl("--columns=name find Logical_Router " + "switch_binding=\'%s\'" % binding_line) + lr_name = column.partition(":")[2].strip() + + for binding in bindings: + lif, ls_uuid = binding.split("=", 1) + switch_binding = lr_name + "-" + lif.strip() + if New_Switch_Bindings.has_key(switch_binding): + ovs.util.ovs_fatal(0, "find duplicate lr-lif bindings (%s-%s)" % (lr_name, lif), vlog) + + ls_name = vtep_ctl("get Logical_Switch %s name" % ls_uuid).strip("\"") + New_Switch_Bindings[switch_binding] = ls_name + + return New_Switch_Bindings + def handle_physical(): # Gather physical ports except the patch ports we created ovs_ports = ovs_vsctl("list-ports %s" % ps_name).split() @@ -568,6 +858,7 @@ def handle_physical(): for b in binding_set: vlan, ls_name = b.split() if ls_name not in Lswitches: + vlog.info("add ls %s" % (ls_name)) Lswitches[ls_name] = Logical_Switch(ls_name) binding = "%s-%s" % (vlan, pp_name) @@ -582,7 +873,6 @@ def handle_physical(): add_binding(binding, ls) - dead_bindings = set(Bindings.keys()).difference(new_bindings) for binding in dead_bindings: ls_name = Bindings[binding] @@ -596,6 +886,52 @@ def handle_physical(): vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name) del Lswitches[ls_name] + # update logical router + new_switch_bindings = get_switch_bindings() + for b in new_switch_bindings: + lr_name, lif = b.split("-",1) + ls_name = new_switch_bindings[b] + + if lr_name not in Lrouters: + vlog.info("add lr %s " % (lr_name)) + Lrouters[lr_name] = Logical_Router(lr_name) + + if ls_name not in Lswitches: + vlog.info("add ls %s " % (ls_name)) + Lswitches[ls_name] = Logical_Switch(ls_name) + + lr = Lrouters[lr_name] + ls = Lswitches[ls_name] + + if Switch_Bindings.has_key(b): + if Switch_Bindings[b] == ls_name: + continue + else: + del_switch_binding(switch_binding, Lswitches[Switch_Bindings[b]]) + + Switch_Bindings[b] = ls_name + lr.add_switch_binding(b, ls) + + dead_bindings = set(Switch_Bindings.keys()).difference(new_switch_bindings.keys()) + for binding in dead_bindings: + lr_name, lif = binding.split("-",1) + ls_name = Switch_Bindings[binding] + + ls = Lswitches[ls_name] + del_switch_binding(binding, ls) + + if not len(ls.ports): + ls.cleanup_ls() + ovs_vsctl("del-br %s" % Lswitches[ls_name].short_name) + vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name) + del Lswitches[ls_name] + + lr = Lrouters[lr_name] + if not len(lr.ports): + ovs_vsctl("del-br %s" % lr.short_name) + vtep_ctl("clear-local-macs %s" % lr.name) + del Lrouters[lr_name] + def setup(): br_list = ovs_vsctl("list-br").split() if (ps_name not in br_list): @@ -695,6 +1031,9 @@ def main(): for ls_name, ls in Lswitches.items(): ls.run() + for lr_name, lr in Lrouters.items(): + lr.run() + run_bfd() poller = ovs.poller.Poller() diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c index 604d19d..25e9bec 100644 --- a/vtep/vtep-ctl.c +++ b/vtep/vtep-ctl.c @@ -88,6 +88,10 @@ static struct vtep_ctl_lswitch *find_lswitch(struct vtep_ctl_context *, const char *name, bool must_exist); +static struct vtep_ctl_lrouter *find_lrouter(struct vtep_ctl_context *, + const char *name, + bool must_exist); + int main(int argc, char *argv[]) { @@ -437,6 +441,8 @@ struct vtep_ctl_context { * struct vtep_ctl_lswitch. */ struct shash plocs; /* Maps from "<encap>+<dst_ip>" to * struct vteprec_physical_locator. */ + struct shash lrouters; /* Maps from logical router name to + * struct vtep_ctl_lswitch. */ }; /* Casts 'base' into 'struct vtep_ctl_context'. */ @@ -468,6 +474,15 @@ struct vtep_ctl_lswitch { struct shash mcast_remote; /* Maps from mac to vtep_ctl_mcast_mac. */ }; +struct vtep_ctl_lrouter { + const struct vteprec_logical_router *lr_cfg; + char *name; + struct shash ucast_local; /* Maps from mac to vteprec_ucast_macs_local. */ + struct shash ucast_remote; /* Maps from mac to vteprec_ucast_macs_remote.*/ + struct shash mcast_local; /* Maps from mac to vtep_ctl_mcast_mac. */ + struct shash mcast_remote; /* Maps from mac to vtep_ctl_mcast_mac. */ +}; + struct vtep_ctl_mcast_mac { const struct vteprec_mcast_macs_local *local_cfg; const struct vteprec_mcast_macs_remote *remote_cfg; @@ -597,6 +612,32 @@ del_cached_lswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_lswitch *ls) free(ls); } +static struct vtep_ctl_lrouter * +add_lrouter_to_cache(struct vtep_ctl_context *vtepctl_ctx, + const struct vteprec_logical_router *lr_cfg) +{ + struct vtep_ctl_lrouter *lr = xmalloc(sizeof *lr); + lr->lr_cfg = lr_cfg; + lr->name = xstrdup(lr_cfg->name); + shash_add(&vtepctl_ctx->lrouters, lr->name, lr); + shash_init(&lr->ucast_local); + shash_init(&lr->ucast_remote); + shash_init(&lr->mcast_local); + shash_init(&lr->mcast_remote); + return lr; +} + +static void +del_cached_lrouter(struct vtep_ctl_context *ctx, struct vtep_ctl_lrouter *lr) +{ + if (lr->lr_cfg) { + vteprec_logical_router_delete(lr->lr_cfg); + } + shash_find_and_delete(&ctx->lrouters, lr->name); + free(lr->name); + free(lr); +} + static void commit_ls_bindings(struct vtep_ctl_port *port) { @@ -848,12 +889,13 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx) struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; const struct vteprec_logical_switch *ls_cfg; + const struct vteprec_logical_router *lr_cfg; const struct vteprec_ucast_macs_local *ucast_local_cfg; const struct vteprec_ucast_macs_remote *ucast_remote_cfg; const struct vteprec_mcast_macs_local *mcast_local_cfg; const struct vteprec_mcast_macs_remote *mcast_remote_cfg; const struct vteprec_tunnel *tunnel_cfg; - struct sset pswitches, ports, lswitches; + struct sset pswitches, ports, lswitches, lrouters; size_t i; if (vtepctl_ctx->cache_valid) { @@ -865,6 +907,7 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx) shash_init(&vtepctl_ctx->ports); shash_init(&vtepctl_ctx->lswitches); shash_init(&vtepctl_ctx->plocs); + shash_init(&vtepctl_ctx->lrouters); sset_init(&pswitches); sset_init(&ports); @@ -892,6 +935,7 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx) sset_destroy(&ports); sset_init(&lswitches); + sset_init(&lrouters); VTEPREC_LOGICAL_SWITCH_FOR_EACH (ls_cfg, ctx->idl) { if (!sset_add(&lswitches, ls_cfg->name)) { VLOG_WARN("%s: database contains duplicate logical switch name", @@ -902,6 +946,16 @@ vtep_ctl_context_populate_cache(struct ctl_context *ctx) } sset_destroy(&lswitches); + VTEPREC_LOGICAL_ROUTER_FOR_EACH (lr_cfg, ctx->idl) { + if (!sset_add(&lrouters, lr_cfg->name)) { + VLOG_WARN("%s: database contains duplicate logical router name", + lr_cfg->name); + continue; + } + add_lrouter_to_cache(vtepctl_ctx, lr_cfg); + } + sset_destroy(&lrouters); + VTEPREC_UCAST_MACS_LOCAL_FOR_EACH (ucast_local_cfg, ctx->idl) { struct vtep_ctl_lswitch *ls; @@ -1463,6 +1517,64 @@ cmd_unbind_ls(struct ctl_context *ctx) vtep_ctl_context_invalidate_cache(ctx); } +static struct vtep_ctl_lrouter * +find_lrouter(struct vtep_ctl_context *vtepctl_ctx, + const char *name, bool must_exist) +{ + struct vtep_ctl_lrouter *lr; + + ovs_assert(vtepctl_ctx->cache_valid); + + lr = shash_find_data(&vtepctl_ctx->lrouters, name); + if (must_exist && !lr) { + ctl_fatal("no logical router named %s", name); + } + return lr; +} + +static void +cmd_add_lr(struct ctl_context *ctx) +{ + struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); + const char *lr_name = ctx->argv[1]; + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + struct vteprec_logical_router *lr; + + vtep_ctl_context_populate_cache(ctx); + if (find_lrouter(vtepctl_ctx, lr_name, false)) { + if (!may_exist) { + ctl_fatal("cannot create logical switch %s because it " + "already exists", lr_name); + } + return; + } + + lr = vteprec_logical_router_insert(ctx->txn); + vteprec_logical_router_set_name(lr, lr_name); + + vtep_ctl_context_invalidate_cache(ctx); +} + +static void +del_lrouter(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lrouter *lr) +{ + del_cached_lrouter(vtepctl_ctx, lr); +} + +static void +cmd_del_lr(struct ctl_context *ctx) +{ + struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + struct vtep_ctl_lrouter *lr; + + vtep_ctl_context_populate_cache(ctx); + lr = find_lrouter(vtepctl_ctx, ctx->argv[1], must_exist); + if (lr) { + del_lrouter(vtepctl_ctx, lr); + } +} + static void add_ucast_entry(struct ctl_context *ctx, bool local) { @@ -2044,6 +2156,10 @@ static const struct ctl_table_class tables[] = { {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, + {&vteprec_table_logical_router, + {{&vteprec_table_logical_router, &vteprec_logical_router_col_name, NULL}, + {NULL, NULL, NULL}}}, + {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; @@ -2300,6 +2416,10 @@ static const struct ctl_command_syntax vtep_commands[] = { {"bind-ls", 4, 4, NULL, pre_get_info, cmd_bind_ls, NULL, "", RO}, {"unbind-ls", 3, 3, NULL, pre_get_info, cmd_unbind_ls, NULL, "", RO}, + /* Logical Router commands. */ + {"add-lr", 1, 1, NULL, pre_get_info, cmd_add_lr, NULL, "--may-exist", RW}, + {"del-lr", 1, 1, NULL, pre_get_info, cmd_del_lr, NULL, "--if-exists", RW}, + /* MAC binding commands. */ {"add-ucast-local", 3, 4, NULL, pre_get_info, cmd_add_ucast_local, NULL, "", RW}, _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev