ovs-vsctl: New commands for working with external IDs.
authorBen Pfaff <blp@nicira.com>
Thu, 10 Dec 2009 01:04:08 +0000 (17:04 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 10 Dec 2009 01:06:49 +0000 (17:06 -0800)
This aids XenServer integration and should make it easier to integrate
with other environments in the future as well.

tests/ovs-vsctl.at
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/vswitch-idl.ovsidl

index 2437d64..a76fd48 100644 (file)
@@ -271,6 +271,62 @@ CHECK_PORTS([a])
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
+AT_SETUP([external IDs])
+AT_KEYWORDS([ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+  [--oneline add-br a], 
+  [add-port a a1],
+  [add-bond a bond0 a2 a3],
+  [br-set-external-id a key0 value0],
+  [port-set-external-id a1 key1 value1],
+  [iface-set-external-id a2 key2 value2],
+  [iface-set-external-id a2 key3 value3],
+  [iface-set-external-id a3 key4 value4],
+  [br-get-external-id a],
+  [br-get-external-id a key0],
+  [br-get-external-id a key1],
+  [br-set-external-id a key0 othervalue],
+  [br-get-external-id a],
+  [br-set-external-id a key0],
+  [br-get-external-id a],
+  [port-get-external-id a1],
+  [iface-get-external-id a2],
+  [iface-get-external-id a3])], [0], [
+
+
+
+
+
+
+
+key0=value0
+value0
+
+
+key0=othervalue
+
+
+key1=value1
+key2=value2\nkey3=value3
+key4=value4
+], [], [OVS_VSCTL_CLEANUP])
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+  [--oneline br-get-external-id a],
+  [port-get-external-id a1],
+  [iface-get-external-id a2],
+  [iface-get-external-id a3])], [0],
+[
+key1=value1
+key2=value2\nkey3=value3
+key4=value4
+], [], [OVS_VSCTL_CLEANUP])
+CHECK_BRIDGES([a, a, 0])
+CHECK_PORTS([a], [a1], [bond0])
+CHECK_IFACES([a], [a1], [a2], [a3])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
 dnl ----------------------------------------------------------------------
 AT_BANNER([ovs-vsctl unit tests -- fake bridges])
 
@@ -315,6 +371,31 @@ CHECK_BRIDGES
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
+AT_SETUP([simple fake bridge + external IDs])
+AT_KEYWORDS([ovs-vsctl fake-bridge])
+OVS_VSCTL_SETUP
+OVS_VSCTL_SETUP_SIMPLE_FAKE_CONF
+AT_CHECK([RUN_OVS_VSCTL_TOGETHER(
+  [--oneline br-set-external-id xenbr0 key0 value0],
+  [br-set-external-id xapi1 key1 value1],
+  [br-get-external-id xenbr0],
+  [br-get-external-id xenbr0 key0],
+  [br-get-external-id xapi1],
+  [br-get-external-id xapi1 key1])], [0], [
+
+key0=value0
+value0
+key1=value1
+value1
+], [], [OVS_VSCTL_CLEANUP])
+CHECK_BRIDGES([xapi1, xenbr0, 9], [xenbr0, xenbr0, 0])
+CHECK_PORTS([xenbr0], [eth0])
+CHECK_IFACES([xenbr0], [eth0])
+CHECK_PORTS([xapi1], [eth0.9])
+CHECK_IFACES([xapi1], [eth0.9])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
 m4_define([OVS_VSCTL_SETUP_BOND_FAKE_CONF],
   [AT_CHECK(
      [RUN_OVS_VSCTL(
index 84e7294..db0b9b9 100644 (file)
@@ -131,6 +131,25 @@ decimal integer.  If \fIbridge\fR is a real bridge, prints 0.
 If \fIbridge\fR is a fake bridge, prints the name of its parent
 bridge.  If \fIbridge\fR is a real bridge, print \fIbridge\fR.
 .
+.IP "\fBbr\-set\-external\-id \fIbridge key\fR [\fIvalue\fR]"
+Sets or clears an ``external ID'' value on \fIbridge\fR.  These values
+are intended to identify entities external to Open vSwitch with which
+\fIbridge\fR is associated, e.g. the bridge's identifier in a
+virtualization management platform.  The Open vSwitch database schema
+specifies well-known \fIkey\fR values, but \fIkey\fR and \fIvalue\fR
+are otherwise arbitrary strings.
+.IP
+If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for
+\fIbridge\fR, overwriting any previous value.  If \fIvalue\fR is
+omitted, then \fIkey\fR is removed from \fIbridge\fR's set of external
+IDs (if it was present).
+.
+.IP "\fBbr\-get\-external\-id \fIbridge\fR [\fIkey\fR]"
+Queries the external IDs on \fIbridge\fR.  If \fIkey\fR is specified,
+the output is the value for that \fIkey\fR or the empty string if
+\fIkey\fR is unset.  If \fIkey\fR is omitted, the output is
+\fIkey\fB=\fIvalue\fR, one per line, for each key-value pair.
+.
 .SS "Port Commands"
 .
 These commands examine and manipulate Open vSwitch ports.  These
@@ -158,6 +177,25 @@ must be the real or fake bridge that contains \fIport\fR.
 Prints the name of the bridge that contains \fIport\fR on standard
 output.
 .
+.IP "\fBport\-set\-external\-id \fIport key\fR [\fIvalue\fR]"
+Sets or clears an ``external ID'' value on \fIport\fR.  These value
+are intended to identify entities external to Open vSwitch with which
+\fIport\fR is associated, e.g. the port's identifier in a
+virtualization management platform.  The Open vSwitch database schema
+specifies well-known \fIkey\fR values, but \fIkey\fR and \fIvalue\fR
+are otherwise arbitrary strings.
+.IP
+If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for
+\fIport\fR, overwriting any previous value.  If \fIvalue\fR is
+omitted, then \fIkey\fR is removed from \fIport\fR's set of external
+IDs (if it was present).
+.
+.IP "\fBbr\-get\-external\-id \fIport\fR [\fIkey\fR]"
+Queries the external IDs on \fIport\fR.  If \fIkey\fR is specified,
+the output is the value for that \fIkey\fR or the empty string if
+\fIkey\fR is unset.  If \fIkey\fR is omitted, the output is
+\fIkey\fB=\fIvalue\fR, one per line, for each key-value pair.
+.
 .SS "Interface Commands"
 .
 These commands examine the interfaces attached to an Open vSwitch
@@ -172,6 +210,26 @@ list.
 .IP "\fBiface\-to\-br \fIiface\fR"
 Prints the name of the bridge that contains \fIiface\fR on standard
 output.
+.
+.IP "\fBiface\-set\-external\-id \fIiface key\fR [\fIvalue\fR]"
+Sets or clears an ``external ID'' value on \fIiface\fR.  These value
+are intended to identify entities external to Open vSwitch with which
+\fIiface\fR is associated, e.g. the interface's identifier in a
+virtualization management platform.  The Open vSwitch database schema
+specifies well-known \fIkey\fR values, but \fIkey\fR and \fIvalue\fR
+are otherwise arbitrary strings.
+.IP
+If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for
+\fIiface\fR, overwriting any previous value.  If \fIvalue\fR is
+omitted, then \fIkey\fR is removed from \fIiface\fR's set of external
+IDs (if it was present).
+.
+.IP "\fBbr\-get\-external\-id \fIiface\fR [\fIkey\fR]"
+Queries the external IDs on \fIiface\fR.  If \fIkey\fR is specified,
+the output is the value for that \fIkey\fR or the empty string if
+\fIkey\fR is unset.  If \fIkey\fR is omitted, the output is
+\fIkey\fB=\fIvalue\fR, one per line, for each key-value pair.
+.
 .SH "EXAMPLES"
 Create a new bridge named br0 and add port eth0 to it:
 .IP
index 0f6abf3..70e12e6 100644 (file)
@@ -717,6 +717,130 @@ cmd_br_exists(int argc UNUSED, char *argv[],
     free_info(&info);
 }
 
+/* Returns true if 'b_prefix' (of length 'b_prefix_len') concatenated with 'b'
+ * equals 'a', false otherwise. */
+static bool
+key_matches(const char *a,
+            const char *b_prefix, size_t b_prefix_len, const char *b)
+{
+    return !strncmp(a, b_prefix, b_prefix_len) && !strcmp(a + b_prefix_len, b);
+}
+
+static void
+set_external_id(char **old_keys, char **old_values, size_t old_n,
+                char *key, char *value,
+                char ***new_keysp, char ***new_valuesp, size_t *new_np)
+{
+    char **new_keys;
+    char **new_values;
+    size_t new_n;
+    size_t i;
+
+    new_keys = xmalloc(sizeof *new_keys * (old_n + 1));
+    new_values = xmalloc(sizeof *new_values * (old_n + 1));
+    new_n = 0;
+    for (i = 0; i < old_n; i++) {
+        if (strcmp(key, old_keys[i])) {
+            new_keys[new_n] = old_keys[i];
+            new_values[new_n] = old_values[i];
+            new_n++;
+        }
+    }
+    if (value) {
+        new_keys[new_n] = key;
+        new_values[new_n] = value;
+        new_n++;
+    }
+    *new_keysp = new_keys;
+    *new_valuesp = new_values;
+    *new_np = new_n;
+}
+
+static void
+cmd_br_set_external_id(int argc, char *argv[],
+                       const struct ovsrec_open_vswitch *ovs,
+                       struct ds *output UNUSED)
+{
+    struct vsctl_info info;
+    struct vsctl_bridge *bridge;
+    char **keys, **values;
+    size_t n;
+
+    get_info(ovs, &info);
+    bridge = find_bridge(&info, argv[1]);
+    if (bridge->br_cfg) {
+        set_external_id(bridge->br_cfg->key_external_ids,
+                        bridge->br_cfg->value_external_ids,
+                        bridge->br_cfg->n_external_ids,
+                        argv[2], argc >= 4 ? argv[3] : NULL,
+                        &keys, &values, &n);
+        ovsrec_bridge_set_external_ids(bridge->br_cfg, keys, values, n);
+    } else {
+        char *key = xasprintf("fake-bridge-%s", argv[2]);
+        struct vsctl_port *port = shash_find_data(&info.ports, argv[1]);
+        set_external_id(port->port_cfg->key_external_ids,
+                        port->port_cfg->value_external_ids,
+                        port->port_cfg->n_external_ids,
+                        key, argc >= 4 ? argv[3] : NULL,
+                        &keys, &values, &n);
+        ovsrec_port_set_external_ids(port->port_cfg, keys, values, n);
+        free(key);
+    }
+    free(keys);
+    free(values);
+
+    free_info(&info);
+}
+
+static void
+get_external_id(char **keys, char **values, size_t n,
+                const char *prefix, const char *key,
+                struct ds *output)
+{
+    size_t prefix_len = strlen(prefix);
+    struct svec svec;
+    size_t i;
+
+    svec_init(&svec);
+    for (i = 0; i < n; i++) {
+        if (!key && !strncmp(keys[i], prefix, prefix_len)) {
+            svec_add_nocopy(&svec, xasprintf("%s=%s",
+                                             keys[i] + prefix_len, values[i]));
+        } else if (key_matches(keys[i], prefix, prefix_len, key)) {
+            svec_add(&svec, values[i]);
+            break;
+        }
+    }
+    output_sorted(&svec, output);
+    svec_destroy(&svec);
+}
+
+static void
+cmd_br_get_external_id(int argc, char *argv[],
+                       const struct ovsrec_open_vswitch *ovs,
+                       struct ds *output)
+{
+    struct vsctl_info info;
+    struct vsctl_bridge *bridge;
+
+    get_info(ovs, &info);
+    bridge = find_bridge(&info, argv[1]);
+    if (bridge->br_cfg) {
+        get_external_id(bridge->br_cfg->key_external_ids,
+                        bridge->br_cfg->value_external_ids,
+                        bridge->br_cfg->n_external_ids,
+                        "", argc >= 3 ? argv[2] : NULL, output);
+    } else {
+        struct vsctl_port *port = shash_find_data(&info.ports, argv[1]);
+        get_external_id(port->port_cfg->key_external_ids,
+                        port->port_cfg->value_external_ids,
+                        port->port_cfg->n_external_ids,
+                        "fake-bridge-", argc >= 3 ? argv[2] : NULL, output);
+    }
+    free_info(&info);
+}
+
+
 static void
 cmd_list_ports(int argc UNUSED, char *argv[],
                const struct ovsrec_open_vswitch *ovs, struct ds *output)
@@ -835,6 +959,47 @@ cmd_port_to_br(int argc UNUSED, char *argv[],
     free_info(&info);
 }
 
+static void
+cmd_port_set_external_id(int argc, char *argv[],
+                         const struct ovsrec_open_vswitch *ovs,
+                         struct ds *output UNUSED)
+{
+    struct vsctl_info info;
+    struct vsctl_port *port;
+    char **keys, **values;
+    size_t n;
+
+    get_info(ovs, &info);
+    port = find_port(&info, argv[1]);
+    set_external_id(port->port_cfg->key_external_ids,
+                    port->port_cfg->value_external_ids,
+                    port->port_cfg->n_external_ids,
+                    argv[2], argc >= 4 ? argv[3] : NULL,
+                    &keys, &values, &n);
+    ovsrec_port_set_external_ids(port->port_cfg, keys, values, n);
+    free(keys);
+    free(values);
+
+    free_info(&info);
+}
+
+static void
+cmd_port_get_external_id(int argc, char *argv[],
+                         const struct ovsrec_open_vswitch *ovs,
+                         struct ds *output)
+{
+    struct vsctl_info info;
+    struct vsctl_port *port;
+
+    get_info(ovs, &info);
+    port = find_port(&info, argv[1]);
+    get_external_id(port->port_cfg->key_external_ids,
+                    port->port_cfg->value_external_ids,
+                    port->port_cfg->n_external_ids,
+                    "",  argc >= 3 ? argv[2] : NULL, output);
+    free_info(&info);
+}
+
 static void
 cmd_br_to_vlan(int argc UNUSED, char *argv[],
                const struct ovsrec_open_vswitch *ovs, struct ds *output)
@@ -903,6 +1068,47 @@ cmd_iface_to_br(int argc UNUSED, char *argv[],
     ds_put_format(output, "%s\n", iface->port->bridge->name);
     free_info(&info);
 }
+
+static void
+cmd_iface_set_external_id(int argc, char *argv[],
+                         const struct ovsrec_open_vswitch *ovs,
+                         struct ds *output UNUSED)
+{
+    struct vsctl_info info;
+    struct vsctl_iface *iface;
+    char **keys, **values;
+    size_t n;
+
+    get_info(ovs, &info);
+    iface = find_iface(&info, argv[1]);
+    set_external_id(iface->iface_cfg->key_external_ids,
+                    iface->iface_cfg->value_external_ids,
+                    iface->iface_cfg->n_external_ids,
+                    argv[2], argc >= 4 ? argv[3] : NULL,
+                    &keys, &values, &n);
+    ovsrec_interface_set_external_ids(iface->iface_cfg, keys, values, n);
+    free(keys);
+    free(values);
+
+    free_info(&info);
+}
+
+static void
+cmd_iface_get_external_id(int argc, char *argv[],
+                          const struct ovsrec_open_vswitch *ovs,
+                          struct ds *output)
+{
+    struct vsctl_info info;
+    struct vsctl_iface *iface;
+
+    get_info(ovs, &info);
+    iface = find_iface(&info, argv[1]);
+    get_external_id(iface->iface_cfg->key_external_ids,
+                    iface->iface_cfg->value_external_ids,
+                    iface->iface_cfg->n_external_ids,
+                    "",  argc >= 3 ? argv[2] : NULL, output);
+    free_info(&info);
+}
 \f
 typedef void vsctl_handler_func(int argc, char *argv[],
                                 const struct ovsrec_open_vswitch *,
@@ -1016,19 +1222,30 @@ static vsctl_handler_func *
 get_vsctl_handler(int argc, char *argv[])
 {
     static const struct vsctl_command all_commands[] = {
+        /* Bridge commands. */
         {"add-br", 1, 3, cmd_add_br},
         {"del-br", 1, 1, cmd_del_br},
         {"list-br", 0, 0, cmd_list_br},
         {"br-exists", 1, 1, cmd_br_exists},
+        {"br-to-vlan", 1, 1, cmd_br_to_vlan},
+        {"br-to-parent", 1, 1, cmd_br_to_parent},
+        {"br-set-external-id", 2, 3, cmd_br_set_external_id},
+        {"br-get-external-id", 1, 2, cmd_br_get_external_id},
+
+        /* Port commands. */
         {"list-ports", 1, 1, cmd_list_ports},
         {"add-port", 2, 2, cmd_add_port},
         {"add-bond", 4, INT_MAX, cmd_add_bond},
         {"del-port", 1, 2, cmd_del_port},
         {"port-to-br", 1, 1, cmd_port_to_br},
-        {"br-to-vlan", 1, 1, cmd_br_to_vlan},
-        {"br-to-parent", 1, 1, cmd_br_to_parent},
+        {"port-set-external-id", 2, 3, cmd_port_set_external_id},
+        {"port-get-external-id", 1, 2, cmd_port_get_external_id},
+
+        /* Interface commands. */
         {"list-ifaces", 1, 1, cmd_list_ifaces},
         {"iface-to-br", 1, 1, cmd_iface_to_br},
+        {"iface-set-external-id", 2, 3, cmd_iface_set_external_id},
+        {"iface-get-external-id", 1, 2, cmd_iface_get_external_id},
     };
 
     const struct vsctl_command *p;
index 76cadab..1df9e92 100644 (file)
          "type": {"key": "uuid", "keyRefTable": "NetFlow", "min": 0, "max": 1}},
        "controller": {
          "comment": "OpenFlow controller.  If unset, defaults to that specified by the parent Open_vSwitch.",
-         "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}}}},
+         "type": {"key": "uuid", "keyRefTable": "Controller", "min": 0, "max": 1}},
+       "external_ids": {
+         "comment": "Key-value pairs that identify this bridge's role in external systems.  The currently defined key-value pairs are: \"xs-network-uuid\", a space-delimited set of the Citrix XenServer network UUIDs with which this bridge is associated.",
+         "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}},
    "Port": {
      "comment": "A port within a Bridge.  May contain a single Interface or multiple (bonded) Interfaces.",
      "columns": {
          "type": "boolean"},
        "fake_bridge": {
          "comment": "Does this port represent a sub-bridge for its tagged VLAN within the Bridge?  See ovs-vsctl(8) for more information.",
-         "type": "boolean"}}},
+         "type": "boolean"},
+       "external_ids": {
+         "comment": "Key-value pairs that identify this port's role in external systems.  No key-value pairs native to Port are currently defined.  For fake bridges (see the \"fake-bridge\" column), external IDs for the fake bridge are defined here by prefixing their keys with \"fake-bridge\", e.g. \"fake-bridge-xs-network-uuids\".",
+         "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}},
    "Interface": {
      "comment": "An interface within a Port.",
      "columns": {
          "comment": "Ethernet address to set for this interface.  If unset then the default MAC address is used.  May not be supported on all interfaces.  Exactly 12 hex digits in the form XX:XX:XX:XX:XX:XX.",
          "type": {"key": "string", "min": 0, "max": 1}},
        "external_ids": {
-         "comment": "Key-value pairs that identify this interface's role in external systems.  The only currently defined key is \"xs-vif-uuid\", whose value is the UUID of the Citrix XenServer VIF associated with this interface.",
+         "comment": "Key-value pairs that identify this interface's role in external systems.  The currently defined key-value pairs are: \"xs-vif-uuid\", the UUID of the Citrix XenServer VIF associated with this interface; \"xs-network-uuid\", the UUID of the Citrix XenServer network to which this interface is attached; \"xs-vif-vm-uuid\", the UUID of the Citrix XenServer VM to which this interface belongs; \"xs-vif-mac\", the value of the \"MAC\" field in the Citrix XenServer VIF record for this interface.",
          "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}},
        "ofport": {
          "comment": "OpenFlow port number for this interface.  This is populated when the port number becomes known.  Before it is populated its value will be missing.  If the interface cannot be added then this is indicated by a value of -1.",