Signed-off-by: Shuangmin Zhang <[email protected]>
---
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
[email protected]
http://openvswitch.org/mailman/listinfo/dev