From patchwork Thu Nov 2 16:13:56 2017
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Mark Michelson
These flows reply to ARP requests for the virtual IP addresses
configured in the router for DNAT or load balancing. For a
- configured DNAT IP address or a load balancer VIP A,
+ configured DNAT IP address or a load balancer IPv4 VIP A,
for each router port P with Ethernet
address E, a priority-90 flow matches
Reply to IPv6 Neighbor Solicitations. These flows reply to
Neighbor Solicitation requests for the router's own IPv6
- address and populate the logical router's mac binding table.
- For each router port P that owns IPv6 address
- A, solicited node address S, and
- Ethernet address E, a priority-90 flow matches
-
Following load balancing DNAT flows are added for Gateway router or
Router with gateway port. These flows are programmed only on the
- OVN_Northbound
database for a logical switch datapath, a
priority-100 flow is added for each configured virtual IP address
- VIP with a match ip && ip4.dst == VIP
-
that sets an action reg0[0] = 1; next;
to act as a
+ VIP. For IPv4 VIPs, the match is ip
+ && ip4.dst == VIP
. For IPv6 VIPs,
+ the match is ip && ip6.dst == VIP
. The
+ flow sets an action reg0[0] = 1; next;
to act as a
hint for table Pre-stateful
to send IP packets to the
connection tracker for packet de-fragmentation before eventually
advancing to ingress table LB
.
@@ -383,21 +385,29 @@
OVN_Northbound
database that includes a L4 port
- PORT of protocol P and IPv4 address
- VIP, a priority-120 flow that matches on
- ct.new && ip && ip4.dst == VIP
- && P && P.dst == PORT
-
with an action of ct_lb(args)
,
- where args contains comma separated IPv4 addresses (and
- optional port numbers) to load balance to.
+ PORT of protocol P and IP address
+ VIP, a priority-120 flow is added. For IPv4 VIPs
+ , the flow matches ct.new && ip &&
+ ip4.dst == VIP && P &&
+ P.dst == PORT
. For IPv6 VIPs,
+ the flow matches ct.new && ip && ip6.dst ==
+ VIP && P && P.dst ==
+ PORT
. The flow's action is ct_lb(args)
+
, where args contains comma separated IP addresses
+ (and optional port numbers) to load balance to. The address family of
+ the IP addresses of args is the same as the address family
+ of VIP
OVN_Northbound
database that includes just an IP address
- VIP to match on, a priority-110 flow that matches on
- ct.new && ip && ip4.dst == VIP
- with an action of ct_lb(args)
, where
- args contains comma separated IPv4 addresses.
+ VIP to match on, OVN adds a priority-110 flow. For IPv4
+ VIPs, the flow matches ct.new && ip &&
+ ip4.dst == VIP
. For IPv6 VIPs,
+ the flow matches ct.new && ip && ip6.dst ==
+ VIP
. The action on this flow is
+ ct_lb(args)
, where args contains comma
+ separated IP addresses of the same address family as VIP.
inport == P && arp.op == 1 &&
@@ -1190,13 +1200,13 @@ arp.sha = external_mac;
inport == P && nd_ns &&
- ip6.dst == {A, E} && nd.target
- == A
with the following actions:
+ address and load balancing IPv6 VIPs and populate the logical
+ router's mac binding table. For each router port P that
+ owns IPv6 address or has load balancing VIP A, solicited
+ node address S, and Ethernet address E, a
+ priority-90 flow matches inport == P &&
+ nd_ns && ip6.dst == {A, E} &&
+ nd.target == A
with the following actions:
@@ -1364,10 +1374,12 @@ icmp4 {
to the next table. If load balancing rules with virtual IP addresses
(and ports) are configured in
OVN_Northbound
database for a
Gateway router, a priority-100 flow is added for each configured virtual
- IP address VIP with a match ip &&
- ip4.dst == VIP
that sets an action
- ct_next;
to send IP packets to the connection tracker for
- packet de-fragmentation and tracking before sending it to the next table.
+ IP address VIP. For IPv4 VIPs the flow matches
+ ip && ip4.dst == VIP
. For IPv6
+ VIPs, the flow matches ip && ip6.dst ==
+ VIP
. The flow uses the action ct_next;
+ to send IP packets to the connection tracker for packet de-fragmentation
+ and tracking before sending it to the next table.
Ingress Table 3: UNSNAT
@@ -1464,7 +1476,8 @@ icmp4 {
redirect-chassis
.
+ redirect-chassis
. These flows do not get programmed for
+ load balancers with IPv6 VIPs.
@@ -1910,7 +1923,8 @@ arp {
router gateway port with an action
ct_dnat;
. If the
backend IPv4 address B is also configured with L4 port
PORT of protocol P, then the
- match also includes P.src
== PORT.
+ match also includes P.src
== PORT. These
+ flows are not added for load balancers with IPv6 VIPs.
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 268bd60d6..6732ad003 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1562,11 +1562,11 @@ join_logical_ports(struct northd_context *ctx,
static void
ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port);
+ uint16_t *port, int *addr_family);
static void
get_router_load_balancer_ips(const struct ovn_datapath *od,
- struct sset *all_ips)
+ struct sset *all_ips, int *addr_family)
{
if (!od->nbr) {
return;
@@ -1582,7 +1582,8 @@ get_router_load_balancer_ips(const struct ovn_datapath *od,
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ addr_family);
if (!ip_address) {
continue;
}
@@ -1659,7 +1660,8 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)
/* A set to hold all load-balancer vips. */
struct sset all_ips = SSET_INITIALIZER(&all_ips);
- get_router_load_balancer_ips(op->od, &all_ips);
+ int addr_family;
+ get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
const char *ip_address;
SSET_FOR_EACH (ip_address, &all_ips) {
@@ -2902,44 +2904,33 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
* 'ip_address'. */
static void
ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port)
+ uint16_t *port, int *addr_family)
{
- char *ip_str, *start, *next;
- *ip_address = NULL;
- *port = 0;
+ struct sockaddr_storage ss;
+ char ip_addr_buf[INET6_ADDRSTRLEN];
+ char *error;
- next = start = xstrdup(key);
- ip_str = strsep(&next, ":");
- if (!ip_str || !ip_str[0]) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
- free(start);
- return;
- }
-
- ovs_be32 ip, mask;
- char *error = ip_parse_masked(ip_str, &ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
+ error = ipv46_parse(key, PORT_OPTIONAL, &ss);
+ if (error) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address for load balancer key %s", key);
- free(start);
+ VLOG_WARN_RL(&rl, "bad ip address or port for load balancer key %s",
+ key);
free(error);
return;
}
- int l4_port = 0;
- if (next && next[0]) {
- if (!str_to_int(next, 0, &l4_port) || l4_port < 0 || l4_port > 65535) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip port for load balancer key %s", key);
- free(start);
- return;
- }
+ if (ss.ss_family == AF_INET) {
+ struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss);
+ *port = sin->sin_port == 0 ? 0 : ntohs(sin->sin_port);
+ inet_ntop(AF_INET, &sin->sin_addr, ip_addr_buf, sizeof ip_addr_buf);
+ } else {
+ struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, &ss);
+ *port = sin6->sin6_port == 0 ? 0 : ntohs(sin6->sin6_port);
+ inet_ntop(AF_INET6, &sin6->sin6_addr, ip_addr_buf, sizeof ip_addr_buf);
}
- *port = l4_port;
- *ip_address = strdup(ip_str);
- free(start);
+ *ip_address = xstrdup(ip_addr_buf);
+ *addr_family = ss.ss_family;
}
/*
@@ -2967,6 +2958,7 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
struct sset all_ips = SSET_INITIALIZER(&all_ips);
bool vip_configured = false;
+ int addr_family = AF_INET;
for (int i = 0; i < od->nbs->n_load_balancer; i++) {
struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
struct smap *vips = &lb->vips;
@@ -2978,7 +2970,8 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
@@ -3000,7 +2993,13 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
* packet to conntrack for defragmentation. */
const char *ip_address;
SSET_FOR_EACH(ip_address, &all_ips) {
- char *match = xasprintf("ip && ip4.dst == %s", ip_address);
+ char *match;
+
+ if (addr_family == AF_INET) {
+ match = xasprintf("ip && ip4.dst == %s", ip_address);
+ } else {
+ match = xasprintf("ip && ip6.dst == %s", ip_address);
+ }
ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
free(match);
@@ -3458,10 +3457,12 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
SMAP_FOR_EACH (node, vips) {
uint16_t port = 0;
+ int addr_family;
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
@@ -3469,7 +3470,11 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
/* New connections in Ingress table. */
char *action = xasprintf("ct_lb(%s);", node->value);
struct ds match = DS_EMPTY_INITIALIZER;
- ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
+ } else {
+ ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address);
+ }
if (port) {
if (lb->protocol && !strcmp(lb->protocol, "udp")) {
ds_put_format(&match, " && udp.dst == %d", port);
@@ -4352,7 +4357,7 @@ static void
add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
struct ds *match, struct ds *actions, int priority,
const char *lb_force_snat_ip, char *backend_ips,
- bool is_udp)
+ bool is_udp, int addr_family)
{
/* A match and actions for new connections. */
char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
@@ -4380,7 +4385,8 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
free(new_match);
free(est_match);
- if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) {
+ if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips
+ || addr_family != AF_INET) {
return;
}
@@ -4397,7 +4403,9 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
while (ip_str && ip_str[0]) {
char *ip_address = NULL;
uint16_t port = 0;
- ip_address_and_port_from_lb_key(ip_str, &ip_address, &port);
+ int addr_family;
+ ip_address_and_port_from_lb_key(ip_str, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
break;
}
@@ -4635,36 +4643,55 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
/* A set to hold all load-balancer vips that need ARP responses. */
struct sset all_ips = SSET_INITIALIZER(&all_ips);
- get_router_load_balancer_ips(op->od, &all_ips);
+ int addr_family;
+ get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
const char *ip_address;
SSET_FOR_EACH(ip_address, &all_ips) {
- ovs_be32 ip;
- if (!ip_parse(ip_address, &ip) || !ip) {
- continue;
- }
-
ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
- op->json_key, IP_ARGS(ip));
+ if (addr_family == AF_INET) {
+ ds_put_format(&match,
+ "inport == %s && arp.tpa == %s && arp.op == 1",
+ op->json_key, ip_address);
+ } else {
+ ds_put_format(&match,
+ "inport == %s && nd_ns && nd.target == %s",
+ op->json_key, ip_address);
+ }
ds_clear(&actions);
- ds_put_format(&actions,
+ if (addr_family == AF_INET) {
+ ds_put_format(&actions,
"eth.dst = eth.src; "
"eth.src = %s; "
"arp.op = 2; /* ARP reply */ "
"arp.tha = arp.sha; "
"arp.sha = %s; "
"arp.tpa = arp.spa; "
- "arp.spa = "IP_FMT"; "
+ "arp.spa = %s; "
"outport = %s; "
"flags.loopback = 1; "
"output;",
op->lrp_networks.ea_s,
op->lrp_networks.ea_s,
- IP_ARGS(ip),
+ ip_address,
op->json_key);
+ } else {
+ ds_put_format(&actions,
+ "nd_na { "
+ "eth.src = %s; "
+ "ip6.src = %s; "
+ "nd.target = %s; "
+ "nd.tll = %s; "
+ "outport = inport; "
+ "flags.loopback = 1; "
+ "output; "
+ "};",
+ op->lrp_networks.ea_s,
+ ip_address,
+ ip_address,
+ op->lrp_networks.ea_s);
+ }
ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
ds_cstr(&match), ds_cstr(&actions));
}
@@ -5328,16 +5355,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
SMAP_FOR_EACH (node, vips) {
uint16_t port = 0;
+ int addr_family;
/* node->key contains IP:port or just IP. */
char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port);
+ ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+ &addr_family);
if (!ip_address) {
continue;
}
if (!sset_contains(&all_ips, ip_address)) {
sset_add(&all_ips, ip_address);
+ /* If there are any load balancing rules, we should send
+ * the packet to conntrack for defragmentation and
+ * tracking. This helps with two things.
+ *
+ * 1. With tracking, we can send only new connections to
+ * pick a DNAT ip address from a group.
+ * 2. If there are L4 ports in load balancing rules, we
+ * need the defragmentation to match on L4 ports. */
+ ds_clear(&match);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ip && ip4.dst == %s",
+ ip_address);
+ } else {
+ ds_put_format(&match, "ip && ip6.dst == %s",
+ ip_address);
+ }
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
+ 100, ds_cstr(&match), "ct_next;");
}
/* Higher priority rules are added for load-balancing in DNAT
@@ -5349,8 +5396,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
ds_put_format(&actions, "ct_lb(%s);", node->value);
ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- ip_address);
+ if (addr_family == AF_INET) {
+ ds_put_format(&match, "ip && ip4.dst == %s",
+ ip_address);
+ } else {
+ ds_put_format(&match, "ip && ip6.dst == %s",
+ ip_address);
+ }
free(ip_address);
int prio = 110;
@@ -5372,26 +5424,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
od->l3redirect_port->json_key);
}
add_router_lb_flow(lflows, od, &match, &actions, prio,
- lb_force_snat_ip, node->value, is_udp);
+ lb_force_snat_ip, node->value, is_udp,
+ addr_family);
}
}
-
- /* If there are any load balancing rules, we should send the
- * packet to conntrack for defragmentation and tracking. This helps
- * with two things.
- *
- * 1. With tracking, we can send only new connections to pick a
- * DNAT ip address from a group.
- * 2. If there are L4 ports in load balancing rules, we need the
- * defragmentation to match on L4 ports. */
- const char *ip_address;
- SSET_FOR_EACH(ip_address, &all_ips) {
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s", ip_address);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
- 100, ds_cstr(&match), "ct_next;");
- }
-
sset_destroy(&all_ips);
}
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 9869d7ed7..a6afc9583 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -113,8 +113,8 @@
- A map of virtual IPv4 addresses (and an optional port number with
+ A map of virtual IP addresses (and an optional port number with
:
as a separator) associated with this load balancer and
- their corresponding endpoint IPv4 addresses (and optional port numbers
+ their corresponding endpoint IP addresses (and optional port numbers
with :
as separators) separated by commas. If
the destination IP address (and port number) of a packet leaving a
- container or a VM matches the virtual IPv4 address (and port number)
+ container or a VM matches the virtual IP address (and port number)
provided here as a key, then OVN will statefully replace the
- destination IP address by one of the provided IPv4 address (and port
- number) in this map as a value. Examples for keys are "192.168.1.4"
- and "172.16.1.8:80". Examples for value are "10.0.0.1, 10.0.0.2" and
+ destination IP address by one of the provided IP address (and port
+ number) in this map as a value. IPv4 and IPv6 addresses are supported
+ for load balancing; however a VIP of one address family may not be
+ mapped to a destination IP address of a different family. If
+ specifying an IPv6 address with a port, the address portion must be
+ enclosed in square brackets. Examples for keys are "192.168.1.4" and
+ "[fd0f::1]:8800". Examples for value are "10.0.0.1, 10.0.0.2" and
"20.0.0.10:8800, 20.0.0.11:8800".
:
as a separator). Examples for vip are
- 192.168.1.4
and 192.168.1.5:8080
.
- ips should be comma separated IPv4 endpoints (or comma
- separated IPv4 addresses and port numbers with :
as a
- separator). Examples for ips are 10.0.0.1,10.0.0.2
-
or 20.0.0.10:8800,20.0.0.11:8800
.
+ 192.168.1.4
, fd0f::1
, and
+ 192.168.1.5:8080
. ips should be comma
+ separated IP endpoints (or comma separated IP addresses and port
+ numbers with :
as a separator). ips must
+ be the same address family as vip. Examples for
+ ips are 10.0.0.1,10.0.0.2
or
+ [fdef::1]:8800,[fdef::2]:8800
.
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 77889961f..34904cf5b 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -1553,40 +1553,76 @@ nbctl_lb_add(struct ctl_context *ctx) } } - ovs_be32 ipv4 = 0; - ovs_be16 port = 0; - char *error = ip_parse_port(lb_vip, &ipv4, &port); + struct sockaddr_storage ss_vip; + char *error; + error = ipv46_parse(lb_vip, PORT_OPTIONAL, &ss_vip); if (error) { free(error); - if (!ip_parse(lb_vip, &ipv4)) { - ctl_fatal("%s: should be an IPv4 address (or an IPv4 address " - "and a port number with : as a separator).", lb_vip); + ctl_fatal("%s: should be an IP address (or an IP address " + "and a port number with : as a separator).", lb_vip); + } + + char lb_vip_normalized[INET6_ADDRSTRLEN + 8]; + char normalized_ip[INET6_ADDRSTRLEN]; + if (ss_vip.ss_family == AF_INET) { + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss_vip); + inet_ntop(AF_INET, &sin->sin_addr, normalized_ip, + sizeof normalized_ip); + if (sin->sin_port) { + is_vip_with_port = true; + snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "%s:%d", + normalized_ip, ntohs(sin->sin_port)); + } else { + is_vip_with_port = false; + ovs_strlcpy(lb_vip_normalized, normalized_ip, + sizeof lb_vip_normalized); } - - if (is_update_proto) { - ctl_fatal("Protocol is unnecessary when no port of vip " - "is given."); + } else { + struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, + &ss_vip); + inet_ntop(AF_INET6, &sin6->sin6_addr, normalized_ip, + sizeof normalized_ip); + if (sin6->sin6_port) { + is_vip_with_port = true; + snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "[%s]:%d", + normalized_ip, ntohs(sin6->sin6_port)); + } else { + is_vip_with_port = false; + ovs_strlcpy(lb_vip_normalized, normalized_ip, + sizeof lb_vip_normalized); } - is_vip_with_port = false; + } + + if (!is_vip_with_port && is_update_proto) { + ctl_fatal("Protocol is unnecessary when no port of vip " + "is given."); } char *token = NULL, *save_ptr = NULL; struct ds lb_ips_new = DS_EMPTY_INITIALIZER; for (token = strtok_r(lb_ips, ",", &save_ptr); token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { - if (is_vip_with_port) { - error = ip_parse_port(token, &ipv4, &port); - if (error) { - free(error); - ds_destroy(&lb_ips_new); - ctl_fatal("%s: should be an IPv4 address and a port " + struct sockaddr_storage ss_dst; + + error = ipv46_parse(token, is_vip_with_port + ? PORT_REQUIRED + : PORT_FORBIDDEN, + &ss_dst); + + if (error) { + free(error); + if (is_vip_with_port) { + ctl_fatal("%s: should be an IP address and a port " "number with : as a separator.", token); + } else { + ctl_fatal("%s: should be an IP address.", token); } - } else { - if (!ip_parse(token, &ipv4)) { - ds_destroy(&lb_ips_new); - ctl_fatal("%s: should be an IPv4 address.", token); - } + } + + if (ss_vip.ss_family != ss_dst.ss_family) { + ds_destroy(&lb_ips_new); + ctl_fatal("%s: IP address family is different from VIP %s.", + token, lb_vip_normalized); } ds_put_format(&lb_ips_new, "%s%s", lb_ips_new.length ? "," : "", token); @@ -1596,19 +1632,19 @@ nbctl_lb_add(struct ctl_context *ctx) if (!add_duplicate) { lb = lb_by_name_or_uuid(ctx, lb_name, false); if (lb) { - if (smap_get(&lb->vips, lb_vip)) { + if (smap_get(&lb->vips, lb_vip_normalized)) { if (!may_exist) { ds_destroy(&lb_ips_new); ctl_fatal("%s: a load balancer with this vip (%s) " - "already exists", lb_name, lb_vip); + "already exists", lb_name, lb_vip_normalized); } /* Update the vips. */ smap_replace(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); } else { /* Add the new vips. */ smap_add(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); } /* Update the load balancer. */ @@ -1628,7 +1664,7 @@ nbctl_lb_add(struct ctl_context *ctx) nbrec_load_balancer_set_name(lb, lb_name); nbrec_load_balancer_set_protocol(lb, lb_proto); smap_add(CONST_CAST(struct smap *, &lb->vips), - lb_vip, ds_cstr(&lb_ips_new)); + lb_vip_normalized, ds_cstr(&lb_ips_new)); nbrec_load_balancer_set_vips(lb, &lb->vips); ds_destroy(&lb_ips_new); } @@ -1670,32 +1706,49 @@ nbctl_lb_del(struct ctl_context *ctx) static void lb_info_add_smap(const struct nbrec_load_balancer *lb, - struct smap *lbs) + struct smap *lbs, int vip_width) { struct ds key = DS_EMPTY_INITIALIZER; struct ds val = DS_EMPTY_INITIALIZER; char *error, *protocol; - ovs_be32 ipv4 = 0; - ovs_be16 port = 0; const struct smap_node **nodes = smap_sort(&lb->vips); if (nodes) { for (int i = 0; i < smap_count(&lb->vips); i++) { const struct smap_node *node = nodes[i]; protocol = lb->protocol; - error = ip_parse_port(node->key, &ipv4, &port); + + struct sockaddr_storage ss; + error = ipv46_parse(node->key, PORT_OPTIONAL, &ss); if (error) { + VLOG_WARN("%s", error); free(error); - protocol = "tcp/udp"; + continue; + } + + if (ss.ss_family == AF_INET) { + struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, + &ss); + if (!sin->sin_port) { + protocol = "tcp/udp"; + } + } else { + struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, + &ss); + if (!sin6->sin6_port) { + protocol = "tcp/udp"; + } } i == 0 ? ds_put_format(&val, - UUID_FMT " %-20.16s%-11.7s%-25.21s%s", + UUID_FMT " %-20.16s%-11.7s%-*.*s%s", UUID_ARGS(&lb->header_.uuid), lb->name, protocol, + vip_width + 4, vip_width, node->key, node->value) - : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s", + : ds_put_format(&val, "\n%60s%-11.7s%-*.*s%s", "", protocol, + vip_width + 4, vip_width, node->key, node->value); } @@ -1709,12 +1762,12 @@ lb_info_add_smap(const struct nbrec_load_balancer *lb, } static void -lb_info_print(struct ctl_context *ctx, struct smap *lbs) +lb_info_print(struct ctl_context *ctx, struct smap *lbs, int vip_width) { const struct smap_node **nodes = smap_sort(lbs); if (nodes) { - ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n", - "UUID", "LB", "PROTO", "VIP", "IPs"); + ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-*.*s%s\n", + "UUID", "LB", "PROTO", vip_width + 4, vip_width, "VIP", "IPs"); for (size_t i = 0; i < smap_count(lbs); i++) { const struct smap_node *node = nodes[i]; ds_put_format(&ctx->output, "%s\n", node->value); @@ -1724,21 +1777,45 @@ lb_info_print(struct ctl_context *ctx, struct smap *lbs) } } +static int +lb_get_max_vip_length(const struct nbrec_load_balancer *lb, int vip_width) +{ + const struct smap_node *node; + int max_length = vip_width; + + SMAP_FOR_EACH (node, &lb->vips) { + size_t keylen = strlen(node->key); + if (max_length < keylen) { + max_length = keylen; + } + } + + return max_length; +} + static void lb_info_list_all(struct ctl_context *ctx, const char *lb_name, bool lb_check) { const struct nbrec_load_balancer *lb; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; + + NBREC_LOAD_BALANCER_FOR_EACH (lb, ctx->idl) { + if (lb_check && strcmp(lb->name, lb_name)) { + continue; + } + vip_width = lb_get_max_vip_length(lb, vip_width); + } NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) { if (lb_check && strcmp(lb->name, lb_name)) { continue; } - lb_info_add_smap(lb, &lbs); + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } @@ -1837,15 +1914,21 @@ nbctl_lr_lb_list(struct ctl_context *ctx) const char *lr_name = ctx->argv[1]; const struct nbrec_logical_router *lr; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; lr = lr_by_name_or_uuid(ctx, lr_name, true); for (int i = 0; i < lr->n_load_balancer; i++) { const struct nbrec_load_balancer *lb = lr->load_balancer[i]; - lb_info_add_smap(lb, &lbs); + vip_width = lb_get_max_vip_length(lb, vip_width); + } + for (int i = 0; i < lr->n_load_balancer; i++) { + const struct nbrec_load_balancer *lb + = lr->load_balancer[i]; + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } @@ -1934,15 +2017,21 @@ nbctl_ls_lb_list(struct ctl_context *ctx) const char *ls_name = ctx->argv[1]; const struct nbrec_logical_switch *ls; struct smap lbs = SMAP_INITIALIZER(&lbs); + int vip_width = 0; ls = ls_by_name_or_uuid(ctx, ls_name, true); for (int i = 0; i < ls->n_load_balancer; i++) { const struct nbrec_load_balancer *lb = ls->load_balancer[i]; - lb_info_add_smap(lb, &lbs); + vip_width = lb_get_max_vip_length(lb, vip_width); + } + for (int i = 0; i < ls->n_load_balancer; i++) { + const struct nbrec_load_balancer *lb + = ls->load_balancer[i]; + lb_info_add_smap(lb, &lbs, vip_width); } - lb_info_print(ctx, &lbs); + lb_info_print(ctx, &lbs, vip_width); smap_destroy(&lbs); } diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index e1c4b4473..26b3ee9d8 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -399,47 +399,43 @@ OVN_NBCTL_TEST_START dnl Add two LBs. AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80a 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:80a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:80a: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:a80 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:a80: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:a80: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10: 192.168.10.10:80,192.168.10.20:80 tcp], [1], [], -[ovn-nbctl: 30.0.0.10:: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.10:: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20 tcp], [1], [], -[ovn-nbctl: 192.168.10.20: should be an IPv4 address and a port number with : as a separator. +[ovn-nbctl: 192.168.10.20: should be an IP address and a port number with : as a separator. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.1a 192.168.10.10:80,192.168.10.20:80], [1], [], -[ovn-nbctl: 30.0.0.1a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 30.0.0.1a: should be an IP address (or an IP address and a port number with : as a separator). ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0 192.168.10.10:80,192.168.10.20:80], [1], [], -[ovn-nbctl: 30.0.0: should be an IPv4 address (or an IPv4 address and a port number with : as a separator). +[ovn-nbctl: 192.168.10.10:80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10,192.168.10.20:80], [1], [], -[ovn-nbctl: 192.168.10.20:80: should be an IPv4 address. +[ovn-nbctl: 192.168.10.20:80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [], -[ovn-nbctl: 192.168.10.10:a80: should be an IPv4 address. +[ovn-nbctl: 192.168.10.10:a80: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:], [1], [], -[ovn-nbctl: 192.168.10.10:: should be an IPv4 address. +[ovn-nbctl: 192.168.10.10:: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.1a], [1], [], -[ovn-nbctl: 192.168.10.1a: should be an IPv4 address. -]) - -AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10], [1], [], -[ovn-nbctl: 192.168.10: should be an IPv4 address. +[ovn-nbctl: 192.168.10.1a: should be an IP address. ]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [], @@ -454,9 +450,9 @@ dnl Add ips to lb AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 ,,,192.168.10.10:80,,,,,]) AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 ,,,192.168.10.10:80,,,,192.168.10.20:80,,,,]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 ]) AT_CHECK([ovn-nbctl lb-del lb0]) AT_CHECK([ovn-nbctl lb-del lb1]) @@ -464,51 +460,51 @@ AT_CHECK([ovn-nbctl lb-del lb1]) AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80]) AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 ]) dnl Update the VIP of the lb1. AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 udp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) dnl Config lb1 with another VIP. AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.20:80 192.168.10.10:80 udp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 - udp 30.0.0.20:80 192.168.10.10:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 + udp 30.0.0.20:80 192.168.10.10:80 ]) AT_CHECK([ovn-nbctl lb-del lb1 30.0.0.20:80]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 ]) dnl Add LBs whose vip is just an IP address. AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.30 192.168.10.10]) AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.30 192.168.10.10]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 -<2> lb2 tcp/udp 30.0.0.30 192.168.10.10 -<3> lb3 tcp/udp 30.0.0.30 192.168.10.10 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +<2> lb2 tcp/udp 30.0.0.30 192.168.10.10 +<3> lb3 tcp/udp 30.0.0.30 192.168.10.10 ]) AT_CHECK([ovn-nbctl lb-del lb2 30.0.0.30]) AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30]) @@ -516,11 +512,11 @@ AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30]) AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 -<2> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 -<3> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 +<2> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +<3> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 ]) dnl If there are multiple load balancers with the same name, use a UUID to update/delete. @@ -538,9 +534,9 @@ AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:9090 192.168.10.10:8080,192 AT_CHECK([ovn-nbctl lb-del lb0 30.0.0.10:80]) AT_CHECK([ovn-nbctl lb-del lb1]) AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 -<1> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +UUID LB PROTO VIP IPs +<0> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 +<1> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 ]) dnl Add load balancer to logical switch. @@ -557,17 +553,17 @@ AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [], AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0]) AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1]) @@ -593,17 +589,17 @@ AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [], AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0]) AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl -UUID LB PROTO VIP IPs -<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 -<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 +UUID LB PROTO VIP IPs +<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 +<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20 ]) AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1]) @@ -620,7 +616,270 @@ AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) OVN_NBCTL_TEST_STOP AT_CLEANUP +dnl +dnl --------------------------------------------------------------------- + +AT_SETUP([ovn-nbctl - LBs IPv6]) +OVN_NBCTL_TEST_START + +dnl A bunch of commands that should fail +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80a [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:80a: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:a80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:a80: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]: [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: [[ae0f::10]]:: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,fd0f::20 tcp], [1], [], +[ovn-nbctl: fd0f::20: should be an IP address and a port number with : as a separator. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10fff [[fd0f::10]]:80,fd0f::20 tcp], [1], [], +[ovn-nbctl: ae0f::10fff: should be an IP address (or an IP address and a port number with : as a separator). +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:80,[[fd0f::20]]:80], [1], [], +[ovn-nbctl: [[fd0f::10]]:80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10,[[fd0f::20]]:80], [1], [], +[ovn-nbctl: [[fd0f::20]]:80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:a80], [1], [], +[ovn-nbctl: [[fd0f::10]]:a80: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:], [1], [], +[ovn-nbctl: [[fd0f::10]]:: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::1001a], [1], [], +[ovn-nbctl: fd0f::1001a: should be an IP address. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10 tcp], [1], [], +[ovn-nbctl: Protocol is unnecessary when no port of vip is given. +]) + + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:900 tcp], [1], [], +[ovn-nbctl: Protocol is unnecessary when no port of vip is given. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [], +[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [], +[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 192.168.10.10:80], [1], [], +[ovn-nbctl: 192.168.10.10:80: IP address family is different from VIP [[ae0f::10]]:80. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 ae0f::10], [1], [], +[ovn-nbctl: ae0f::10: IP address family is different from VIP 30.0.0.10. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 [[ae0f::10]]:80], [1], [], +[ovn-nbctl: [[ae0f::10]]:80: IP address family is different from VIP 30.0.0.10:80. +]) + +AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10]) +AT_CHECK([ovn-nbctl lb-add lb0 ae0f:0000:0000:0000:0000:0000:0000:0010 fd0f::20], +[1], [], [ovn-nbctl: lb0: a load balancer with this vip (ae0f::10) already exists +]) + +AT_CHECK([ovn-nbctl lb-del lb0]) + +dnl Add ips to lb +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,,]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,[[fd0f::20]]:80,,,,]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) +AT_CHECK([ovn-nbctl lb-del lb0]) +AT_CHECK([ovn-nbctl lb-del lb1]) + + +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl Update the VIP of the lb1. +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) + +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) + +dnl Config lb1 with another VIP. +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::20]]:80 [[fd0f::10]]:80 udp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 + udp [[ae0f::20]]:80 [[fd0f::10]]:80 +]) + +AT_CHECK([ovn-nbctl lb-del lb1 [[ae0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +]) +dnl Add LBs whose vip is just an IP address. +AT_CHECK([ovn-nbctl lb-add lb2 ae0f::30 fd0f::10]) +AT_CHECK([ovn-nbctl lb-add lb3 ae0f::30 fd0f::10]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +<2> lb2 tcp/udp ae0f::30 fd0f::10 +<3> lb3 tcp/udp ae0f::30 fd0f::10 +]) +AT_CHECK([ovn-nbctl lb-del lb2 ae0f::30]) +AT_CHECK([ovn-nbctl lb-del lb3 ae0f::30]) + +AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 +<2> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +<3> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl If there are multiple load balancers with the same name, use a UUID to update/delete. +AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) + +AT_CHECK([ovn-nbctl lb-del lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) + +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:8080 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:9090 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp]) +AT_CHECK([ovn-nbctl lb-del lb0 [[ae0f::10]]:80]) +AT_CHECK([ovn-nbctl lb-del lb1]) +AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 +]) + +dnl Add load balancer to logical switch. +AT_CHECK([ovn-nbctl ls-add ls0]) +AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80]) +AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 udp]) +AT_CHECK([ovn-nbctl lb-add lb3 ae0f::10 fd0f::10,fd0f::20]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl --may-exist ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) + +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-del ls0 lb3]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) +AT_CHECK([ovn-nbctl --if-exists ls-lb-del ls0 lb1]) + +dnl Remove all load balancers from logical switch. +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1]) +AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3]) +AT_CHECK([ovn-nbctl ls-lb-del ls0]) +AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) + +dnl Add load balancer to logical router. +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [], +[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID. +]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) + +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl +UUID LB PROTO VIP IPs +<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 +<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20 +]) + +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-del lr0 lb3]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) +AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb1]) + +dnl Remove all load balancers from logical router. +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1]) +AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3]) +AT_CHECK([ovn-nbctl lr-lb-del lr0]) +AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], []) + +OVN_NBCTL_TEST_STOP +AT_CLEANUP dnl --------------------------------------------------------------------- AT_SETUP([ovn-nbctl - basic logical router commands])