Merge commit 'origin/citrix'
authorJustin Pettit <jpettit@nicira.com>
Wed, 29 Jul 2009 05:16:50 +0000 (22:16 -0700)
committerJustin Pettit <jpettit@nicira.com>
Wed, 29 Jul 2009 05:16:50 +0000 (22:16 -0700)
Conflicts:

configure.ac

16 files changed:
debian/openvswitch-switch-config.templates
debian/openvswitch-switch.init
debian/openvswitch-switch.template
debian/po/templates.pot
lib/cfg.c
lib/cfg.h
lib/socket-util.c
lib/vconn.c
utilities/ovs-controller.8.in
utilities/ovs-ofctl.8.in
utilities/ovs-openflowd.8.in
vswitchd/bridge.c
vswitchd/ovs-brcompatd.c
vswitchd/ovs-vswitchd.conf.5.in
xenserver/opt_xensource_libexec_interface-reconfigure
xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py

index 1664682..8ea2d5b 100644 (file)
@@ -113,16 +113,16 @@ Template: openvswitch-switch/controller-vconn
 Type: string
 _Description: Controller location:
  Specify how the OpenFlow switch should connect to the OpenFlow controller.
- The value should be in form "ssl:HOST[:PORT]" to connect to the controller
- over SSL (recommended for security) or "tcp:HOST[:PORT]" to connect over
+ The value should be in form "ssl:IP[:PORT]" to connect to the controller
+ over SSL (recommended for security) or "tcp:IP[:PORT]" to connect over
  cleartext TCP.
 
 Template: openvswitch-switch/controller-vconn-error
 Type: error
 _Description: The controller location is invalid.
- The controller location must be specifed as "ssl:HOST[:PORT]" to
+ The controller location must be specifed as "ssl:IP[:PORT]" to
  connect to the controller over SSL (recommended for security) or
- "tcp:HOST[:PORT]" to connect over cleartext TCP.
+ "tcp:IP[:PORT]" to connect over cleartext TCP.
 
 Template: openvswitch-switch/pki-uri
 Type: string
index ece07a8..da4ec69 100755 (executable)
@@ -220,7 +220,7 @@ case "$1" in
                 configure_ssl
                 ;;
             *)
-                echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]' when not in discovery mode" >&2
+                echo "$default: CONTROLLER must be in the form 'ssl:IP[:PORT]' or 'tcp:IP[:PORT]' when not in discovery mode" >&2
                 echo "Run ovs-switch-setup (in the openvswitch-switch-config package) or edit /etc/default/openvswitch-switch to configure" >&2
                 exit 1
         esac
index 0a72198..f06d4a0 100644 (file)
@@ -69,8 +69,8 @@ SWITCH_IP=dhcp
 
 # CONTROLLER: Location of controller.
 # One of the following formats:
-#  tcp:HOST[:PORT]         via TCP to PORT (default: 6633) on HOST
-#  ssl:HOST[:PORT]         via SSL to PORT (default: 6633) on HOST
+#  tcp:IP[:PORT]         via TCP to PORT (default: 6633) at IP
+#  ssl:IP[:PORT]         via SSL to PORT (default: 6633) at IP
 # The default below assumes that the controller is running locally.
 # This setting has no effect when MODE is set to 'discovery'.
 #CONTROLLER="tcp:127.0.0.1"
index d425b8a..abccbc4 100644 (file)
@@ -278,8 +278,8 @@ msgstr ""
 #: ../openvswitch-switch-config.templates:10001
 msgid ""
 "Specify how the OpenFlow switch should connect to the OpenFlow controller. "
-"The value should be in form \"ssl:HOST[:PORT]\" to connect to the controller "
-"over SSL (recommended for security) or \"tcp:HOST[:PORT]\" to connect over "
+"The value should be in form \"ssl:IP[:PORT]\" to connect to the controller "
+"over SSL (recommended for security) or \"tcp:IP[:PORT]\" to connect over "
 "cleartext TCP."
 msgstr ""
 
@@ -293,8 +293,8 @@ msgstr ""
 #. Description
 #: ../openvswitch-switch-config.templates:11001
 msgid ""
-"The controller location must be specifed as \"ssl:HOST[:PORT]\" to connect "
-"to the controller over SSL (recommended for security) or \"tcp:HOST[:PORT]\" "
+"The controller location must be specifed as \"ssl:IP[:PORT]\" to connect "
+"to the controller over SSL (recommended for security) or \"tcp:IP[:PORT]\" "
 "to connect over cleartext TCP."
 msgstr ""
 
index a53e6e3..433d7a0 100644 (file)
--- a/lib/cfg.c
+++ b/lib/cfg.c
@@ -676,6 +676,25 @@ cfg_del_match(const char *pattern_, ...)
     free(pattern);
 }
 
+/* Fills 'svec' with all of the key-value pairs that match shell glob pattern
+ * 'pattern'.  The caller must first initialize 'svec'. */
+void
+cfg_get_matches(struct svec *svec, const char *pattern_, ...)
+{
+    char *pattern;
+    char **p;
+
+    FORMAT_KEY(pattern_, pattern);
+
+    for (p = cfg.names; *p; p++) {
+        if (!fnmatch(pattern, *p, 0)) {
+            svec_add(svec, *p);
+        }
+    }
+
+    free(pattern);
+}
+
 /* Fills 'svec' with all of the key-value pairs that have sections that
  * begin with 'section'.  The caller must first initialize 'svec'. */
 void
index f548de2..42345f8 100644 (file)
--- a/lib/cfg.h
+++ b/lib/cfg.h
@@ -63,6 +63,8 @@ void cfg_add_entry(const char *key, ...) PRINTF_FORMAT(1, 2);
 void cfg_del_entry(const char *key, ...) PRINTF_FORMAT(1, 2);
 void cfg_del_section(const char *key, ...) PRINTF_FORMAT(1, 2);
 void cfg_del_match(const char *pattern, ...) PRINTF_FORMAT(1, 2);
+void cfg_get_matches(struct svec *svec, const char *pattern, ...)
+    PRINTF_FORMAT(2, 3);
 void cfg_get_section(struct svec *svec, const char *key, ...) 
     PRINTF_FORMAT(2, 3);
 
index d537434..3fcd5a1 100644 (file)
@@ -73,25 +73,16 @@ get_max_fds(void)
     return max_fds;
 }
 
-/* Translates 'host_name', which may be a DNS name or an IP address, into a
- * numeric IP address in '*addr'.  Returns 0 if successful, otherwise a
- * positive errno value. */
+/* Translates 'host_name', which must be a string representation of an IP
+ * address, into a numeric IP address in '*addr'.  Returns 0 if successful,
+ * otherwise a positive errno value. */
 int
 lookup_ip(const char *host_name, struct in_addr *addr) 
 {
     if (!inet_aton(host_name, addr)) {
-        struct hostent *he = gethostbyname(host_name);
-        if (he == NULL) {
-            struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-            VLOG_ERR_RL(&rl, "gethostbyname(%s): %s", host_name,
-                        (h_errno == HOST_NOT_FOUND ? "host not found"
-                         : h_errno == TRY_AGAIN ? "try again"
-                         : h_errno == NO_RECOVERY ? "non-recoverable error"
-                         : h_errno == NO_ADDRESS ? "no address"
-                         : "unknown error"));
-            return ENOENT;
-        }
-        addr->s_addr = *(uint32_t *) he->h_addr;
+        struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
+        return ENOENT;
     }
     return 0;
 }
index ee2fb0d..3210923 100644 (file)
@@ -128,11 +128,11 @@ vconn_usage(bool active, bool passive, bool bootstrap UNUSED)
     printf("\n");
     if (active) {
         printf("Active OpenFlow connection methods:\n");
-        printf("  tcp:HOST[:PORT]         "
-               "PORT (default: %d) on remote TCP HOST\n", OFP_TCP_PORT);
+        printf("  tcp:IP[:PORT]         "
+               "PORT (default: %d) at remote IP\n", OFP_TCP_PORT);
 #ifdef HAVE_OPENSSL
-        printf("  ssl:HOST[:PORT]         "
-               "SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
+        printf("  ssl:IP[:PORT]         "
+               "SSL PORT (default: %d) at remote IP\n", OFP_SSL_PORT);
 #endif
         printf("  unix:FILE               Unix domain socket named FILE\n");
     }
index b6b05d0..658cf13 100644 (file)
@@ -38,16 +38,15 @@ to the given \fIip\fR.
 Listens for connections from OpenFlow switches on the Unix domain
 server socket named \fIfile\fR.
 
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
-
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
+
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
 
 .TP
 \fBunix:\fIfile\fR
index 0b4856e..2e2b38b 100644 (file)
@@ -26,16 +26,15 @@ connecting to an OpenFlow switch.  The following connection methods
 are supported:
 
 .RS
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
-
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
+
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
 
 .TP
 \fBunix:\fIfile\fR
index 3684fab..4d1a858 100644 (file)
@@ -23,16 +23,15 @@ The optional \fIcontroller\fR argument specifies how to connect to
 the OpenFlow controller.  It takes one of the following forms:
 
 .RS
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  The \fB--private-key\fR, \fB--certificate\fR, and
-\fB--ca-cert\fR options are mandatory when this form is used.
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+The \fB--private-key\fR, \fB--certificate\fR, and \fB--ca-cert\fR
+options are mandatory when this form is used.
 
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
 
 .TP
 \fBunix:\fIfile\fR
@@ -348,9 +347,10 @@ mode (see \fBContacting the Controller\fR above).  When neither option
 is given, the default is in-band control.
 
 .TP
-\fB--netflow=\fIhost\fB:\fIport\fR
-Configures the given UDP \fIport\fR on the specified IP \fIhost\fR as
-a recipient of NetFlow messages for expired flows.
+\fB--netflow=\fIip\fB:\fIport\fR
+Configures the given UDP \fIport\fR on the specified IP \fIip\fR as
+a recipient of NetFlow messages for expired flows.  The \fIip\fR must
+be specified numerically, not as a DNS name.
 
 This option may be specified multiple times to configure additional
 NetFlow collectors.
index f977c2b..cd4fde3 100644 (file)
@@ -793,9 +793,12 @@ bridge_unixctl_fdb_show(struct unixctl_conn *conn, const char *args)
     if (br->ml) {
         const struct mac_entry *e;
         LIST_FOR_EACH (e, struct mac_entry, lru_node, &br->ml->lrus) {
+            if (e->port < 0 || e->port >= br->n_ports) {
+                continue;
+            }
             ds_put_format(&ds, "%5d  %4d  "ETH_ADDR_FMT"  %3d\n",
-                          e->port, e->vlan, ETH_ADDR_ARGS(e->mac),
-                          mac_entry_age(e));
+                          br->ports[e->port]->ifaces[0]->dp_ifidx,
+                          e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e));
         }
     }
     unixctl_command_reply(conn, 200, ds_cstr(&ds));
index 16fc786..2384f5c 100644 (file)
@@ -241,9 +241,14 @@ rewrite_and_reload_config(void)
 }
 
 /* Get all the interfaces for 'bridge' as 'ifaces', breaking bonded interfaces
- * down into their constituent parts. */
+ * down into their constituent parts.
+ *
+ * If 'vlan' < 0, all interfaces on 'bridge' are reported.  If 'vlan' == 0,
+ * then only interfaces for trunk ports or ports with implicit VLAN 0 are
+ * reported.  If 'vlan' > 0, only interfaces with implict VLAN 'vlan' are
+ * reported.  */
 static void
-get_bridge_ifaces(const char *bridge, struct svec *ifaces)
+get_bridge_ifaces(const char *bridge, struct svec *ifaces, int vlan)
 {
     struct svec ports;
     int i;
@@ -253,6 +258,15 @@ get_bridge_ifaces(const char *bridge, struct svec *ifaces)
     cfg_get_all_keys(&ports, "bridge.%s.port", bridge);
     for (i = 0; i < ports.n; i++) {
         const char *port_name = ports.names[i];
+        if (vlan >= 0) {
+            int port_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
+            if (port_vlan < 0) {
+                port_vlan = 0;
+            }
+            if (vlan != port_vlan) {
+                continue;
+            }
+        }
         if (cfg_has_section("bonding.%s", port_name)) {
             struct svec slaves;
             svec_init(&slaves);
@@ -288,7 +302,7 @@ prune_ports(void)
         struct svec ifaces;
 
         /* Check that each bridge interface exists. */
-        get_bridge_ifaces(br_name, &ifaces);
+        get_bridge_ifaces(br_name, &ifaces, -1);
         for (j = 0; j < ifaces.n; j++) {
             const char *iface_name = ifaces.names[j];
             enum netdev_flags flags;
@@ -518,6 +532,27 @@ handle_port_cmd(struct ofpbuf *buffer, bool add)
     return error;
 }
 
+/* Returns the name of the bridge that contains a port named 'port_name', as a
+ * malloc'd string that the caller must free, or a null pointer if no bridge
+ * contains a port named 'port_name'. */
+static char *
+get_bridge_containing_port(const char *port_name)
+{
+    struct svec matches;
+    const char *start, *end;
+
+    svec_init(&matches);
+    cfg_get_matches(&matches, "bridge.*.port=%s", port_name);
+    if (!matches.n) {
+        return 0;
+    }
+
+    start = matches.names[0] + strlen("bridge.");
+    end = strstr(start, ".port=");
+    assert(end);
+    return xmemdup0(start, end - start);
+}
+
 static int
 handle_fdb_query_cmd(struct ofpbuf *buffer)
 {
@@ -542,35 +577,65 @@ handle_fdb_query_cmd(struct ofpbuf *buffer)
     int n_local_macs;
     int i;
 
+    /* Impedance matching between the vswitchd and Linux kernel notions of what
+     * a bridge is.  The kernel only handles a single VLAN per bridge, but
+     * vswitchd can deal with all the VLANs on a single bridge.  We have to
+     * pretend that the former is the case even though the latter is the
+     * implementation. */
+    const char *linux_bridge;   /* Name used by brctl. */
+    char *ovs_bridge;           /* Name used by ovs-vswitchd. */
+    int br_vlan;                /* VLAN tag. */
+    struct svec ifaces;
+
     struct ofpbuf query_data;
     char *unixctl_command;
     uint64_t count, skip;
-    const char *br_name;
-    struct svec ifaces;
     char *output;
     char *save_ptr;
     uint32_t seq;
     int error;
 
     /* Parse the command received from brcompat_mod. */
-    error = parse_command(buffer, &seq, &br_name, NULL, &count, &skip);
+    error = parse_command(buffer, &seq, &linux_bridge, NULL, &count, &skip);
     if (error) {
         return error;
     }
 
+    /* Figure out vswitchd bridge and VLAN. */
+    cfg_read();
+    if (bridge_exists(linux_bridge)) {
+        /* Bridge name is the same.  We are interested in VLAN 0. */
+        ovs_bridge = xstrdup(linux_bridge);
+        br_vlan = 0;
+    } else {
+        /* No such Open vSwitch bridge 'linux_bridge', but there might be an
+         * internal port named 'linux_bridge' on some other bridge
+         * 'ovs_bridge'.  If so then we are interested in the VLAN assigned to
+         * port 'linux_bridge' on the bridge named 'ovs_bridge'. */
+        const char *port_name = linux_bridge;
+
+        ovs_bridge = get_bridge_containing_port(port_name);
+        br_vlan = cfg_get_vlan(0, "vlan.%s.tag", port_name);
+        if (!ovs_bridge || br_vlan < 0) {
+            free(ovs_bridge);
+            send_reply(seq, ENODEV, NULL);
+            return error;
+        }
+    }
+
     /* Fetch the forwarding database using ovs-appctl. */
-    unixctl_command = xasprintf("fdb/show %s", br_name);
+    unixctl_command = xasprintf("fdb/show %s", ovs_bridge);
     error = execute_appctl_command(unixctl_command, &output);
     free(unixctl_command);
     if (error) {
+        free(ovs_bridge);
         send_reply(seq, error, NULL);
         return error;
     }
 
     /* Fetch the MAC address for each interface on the bridge, so that we can
      * fill in the is_local field in the response. */
-    cfg_read();
-    get_bridge_ifaces(br_name, &ifaces);
+    get_bridge_ifaces(ovs_bridge, &ifaces, br_vlan);
     local_macs = xmalloc(ifaces.n * sizeof *local_macs);
     n_local_macs = 0;
     for (i = 0; i < ifaces.n; i++) {
@@ -607,6 +672,10 @@ handle_fdb_query_cmd(struct ofpbuf *buffer)
             continue;
         }
 
+        if (vlan != br_vlan) {
+            continue;
+        }
+
         if (skip > 0) {
             skip--;
             continue;
@@ -635,6 +704,7 @@ handle_fdb_query_cmd(struct ofpbuf *buffer)
 
     send_reply(seq, 0, &query_data);
     ofpbuf_uninit(&query_data);
+    free(ovs_bridge);
 
     return 0;
 }
index 7599355..665d3d0 100644 (file)
@@ -317,8 +317,9 @@ NetFlow is a protocol that exports a number of details about terminating
 IP flows, such as the principals involved and duration.  A bridge may be 
 configured to send NetFlow v5 records to NetFlow collectors when flows 
 end.  To enable, define the key \fBnetflow.\fIbridge\fB.host\fR for each 
-collector in the form \fIhost\fB:\fIport\fR.  Records from \fIbridge\fR 
-will be sent to each \fIhost\fR on UDP \fIport\fR.
+collector in the form \fIip\fB:\fIport\fR.  Records from \fIbridge\fR 
+will be sent to each \fIip\fR on UDP \fIport\fR.  The \fIip\fR must
+be specified numerically, not as a DNS name.
 
 The NetFlow messages will use the datapath index for the engine type and id.  
 This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and 
@@ -350,16 +351,15 @@ supports the OpenFlow Management Protocol, such as NOX.  This
 functionality is enabled by setting the key \fBmgmt.controller\fR to one 
 of the following values:
 .
-.TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  SSL must be configured when this form is used (see \fBSSL
+.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]"
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+SSL must be configured when this form is used (see \fBSSL
 Configuration\fR, below).
 .
-.TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+.IP "\fBtcp:\fIip\fR[\fB:\fIport\fR]"
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
 .PP
 The maximum time between attempts to connect to the controller may be
 specified in integral seconds with the \fBmgmt.max-backoff\fR key.  The
@@ -430,15 +430,16 @@ that it receives specifies one or more DNS servers.
 .RE
 .
 .TP
-\fBssl:\fIhost\fR[\fB:\fIport\fR]
-The specified SSL \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.  SSL must be configured when this form is used (see \fBSSL
+\fBssl:\fIip\fR[\fB:\fIport\fR]
+The specified SSL \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
+SSL must be configured when this form is used (see \fBSSL
 Configuration\fR, below).
 .
 .TP
-\fBtcp:\fIhost\fR[\fB:\fIport\fR]
-The specified TCP \fIport\fR (default: 6633) on the given remote
-\fIhost\fR.
+\fBtcp:\fIip\fR[\fB:\fIport\fR]
+The specified TCP \fIport\fR (default: 6633) on the host at the given
+\fIip\fR, which must be expressed as an IP address (not a DNS name).
 .
 .TP
 \fBunix:\fIfile\fR
index 74f06f8..2a32fad 100755 (executable)
@@ -835,6 +835,15 @@ def action_up(pif):
     for physdev in physdevs:
         down_netdev(physdev)
 
+    # If we are bringing up a bond, remove IP addresses from the
+    # slaves (because we are implicitly being asked to take them down).
+    # 
+    # Conversely, if we are bringing up an interface that has bond
+    # masters, remove IP addresses from the bond master (because we
+    # are implicitly being asked to take it down).
+    for bond_pif in bond_slaves + bond_masters:
+        run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0']) 
+
     # Remove all keys related to pif and any bond masters linked to PIF.
     del_ports = [ipdev] + physdevs + bond_masters
     if vlan_slave and bond_master:
@@ -929,6 +938,19 @@ def action_up(pif):
 
     # Update /etc/issue (which contains the IP address of the management interface)
     os.system("/sbin/update-issue")
+
+    if bond_slaves:
+        # There seems to be a race somewhere: without this sleep, using
+        # XenCenter to create a bond that becomes the management interface
+        # fails with "The underlying connection was closed: A connection that
+        # was expected to be kept alive was closed by the server." on every
+        # second or third try, even though /var/log/messages doesn't show
+        # anything unusual.
+        #
+        # The race is probably present even without vswitch, but bringing up a
+        # bond without vswitch involves a built-in pause of 10 seconds or more
+        # to wait for the bond to transition from learning to forwarding state.
+        time.sleep(5)
         
 def action_down(pif):
     rec = db.get_pif_record(pif)    
index 4523139..dbd00a4 100644 (file)
@@ -32,6 +32,17 @@ class VSwitchService:
         if self.processname == None:
             self.processname = name
 
+    def version(self):
+        try:
+            output = ShellPipe(["service", self.name, "version"]).Stdout()
+        except StandardError, e:
+            log.error("version retrieval error: " + str(e))
+            return "<unknown>"
+        for line in output:
+            if self.processname in line:
+                return line.split()[-1]
+        return "<unknown>"
+
     def status(self):
         try:
             output = ShellPipe(["service", self.name, "status"]).Stdout()
@@ -40,12 +51,12 @@ class VSwitchService:
             return "<unknown>"
         if len(output) == 0:
             return "<unknown>"
-        for l in output:
-            if self.processname not in l:
+        for line in output:
+            if self.processname not in line:
                 continue
-            elif "running" in l:
+            elif "running" in line:
                 return "Running"
-            elif "stop" in l:
+            elif "stop" in line:
                 return "Stopped"
             else:
                 return "<unknown>"
@@ -262,8 +273,8 @@ class XSFeatureVSwitch:
 
         inPane.NewLine()
 
-        versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
-        inPane.AddStatusField(Lang("Version", 20), versionStr)
+        inPane.AddStatusField(Lang("Version", 20),
+                              VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
 
         inPane.NewLine()