While exposing the info under <interface/> in previous patches works,
it may work only in cases where interface is configured on the host
(especially if netcf backend is in charge). However, orchestrating
application may want to know the link state and speed even in that
case. That's why we ought to expose this in nodedev XML too:
virsh # nodedev-dumpxml net_eth0_f0_de_f1_2b_1b_f3
<device>
<name>net_eth0_f0_de_f1_2b_1b_f3</name>
<path>/sys/devices/pci0000:00/0000:00:19.0/net/eth0</path>
<parent>pci_0000_00_19_0</parent>
<capability type='net'>
<interface>eth0</interface>
<address>f0:de:f1:2b:1b:f3</address>
<link speed='1000' state='up'/>
<capability type='80203'/>
</capability>
</device>
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
Notes:
Unfortunately, udev doesn't register link changes, so ripping the
cable out won't call our udev event callback. If it were so,
things could be much more simpler...
docs/formatnode.html.in | 6 ++++
docs/schemas/nodedev.rng | 1 +
src/conf/node_device_conf.c | 7 +++-
src/conf/node_device_conf.h | 2 ++
src/node_device/node_device_driver.c | 27 ++++++++++++---
src/node_device/node_device_hal.c | 9 +++++
src/node_device/node_device_hal.h | 1 +
src/node_device/node_device_udev.c | 65 ++++++++++++++++++++++++++++++++++++
src/node_device/node_device_udev.h | 4 +++
9 files changed, 116 insertions(+), 6 deletions(-)
diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in
index b424c96..b27b6bd 100644
--- a/docs/formatnode.html.in
+++ b/docs/formatnode.html.in
@@ -154,6 +154,12 @@
<dd>The interface name tied to this device.</dd>
<dt><code>address</code></dt>
<dd>If present, the MAC address of the device.</dd>
+ <dt><code>link</code></dt>
+ <dd>Optional to reflect the status of the link. It has
+ two optional attributes: <code>speed</code> in Mbit per
+ second and <code>state</code> to tell the state of the
+ link.
+ </dd>
<dt><code>capability</code></dt>
<dd>A network protocol exposed by the device, where the
attribute <code>type</code> can be "80203" for
IEEE
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng
index 81ab4d4..8c70536 100644
--- a/docs/schemas/nodedev.rng
+++ b/docs/schemas/nodedev.rng
@@ -233,6 +233,7 @@
<ref name='mac'/>
</element>
</optional>
+ <ref name="link-speed-state"/>
<zeroOrMore>
<ref name='subcapnet'/>
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index e65b5e4..8405a25 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -386,6 +386,7 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
if (data->net.address)
virBufferEscapeString(&buf,
"<address>%s</address>\n",
data->net.address);
+ virInterfaceLinkFormat(&buf, &data->net.lnk);
if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
const char *subtyp =
virNodeDevNetCapTypeToString(data->net.subtype);
@@ -837,7 +838,7 @@ virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr node,
union _virNodeDevCapData *data)
{
- xmlNodePtr orignode;
+ xmlNodePtr orignode, lnk;
int ret = -1;
char *tmp;
@@ -869,6 +870,10 @@ virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt,
data->net.subtype = val;
}
+ lnk = virXPathNode("./link", ctxt);
+ if (lnk && virInterfaceLinkParseXML(lnk, &data->net.lnk) < 0)
+ goto out;
+
ret = 0;
out:
ctxt->node = orignode;
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 50e6805..462bfa4 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -29,6 +29,7 @@
# include "virutil.h"
# include "virthread.h"
# include "virpci.h"
+# include "device_conf.h"
# include <libxml/tree.h>
@@ -135,6 +136,7 @@ struct _virNodeDevCapsDef {
char *address;
unsigned int address_len;
char *ifname;
+ virInterfaceLink lnk;
virNodeDevNetCapType subtype; /* LAST -> no subtype */
} net;
struct {
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index 6906463..f9f0db4 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -36,21 +36,37 @@
#include "virstring.h"
#include "node_device_conf.h"
#include "node_device_hal.h"
+#include "node_device_udev.h"
#include "node_device_driver.h"
#include "virutil.h"
#include "viraccessapicheck.h"
#define VIR_FROM_THIS VIR_FROM_NODEDEV
+static int
+update_link(virNodeDeviceDefPtr def)
+{
+#ifdef WITH_UDEV
+ return udevUpdateLink(def);
+#endif
+#ifdef WITH_HAL
+ return halUpdateLink(def);
+#endif
+}
+
static int update_caps(virNodeDeviceObjPtr dev)
{
virNodeDevCapsDefPtr cap = dev->def->caps;
while (cap) {
- /* The only caps that currently need updating are FC related. */
- if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) {
- detect_scsi_host_caps(&dev->def->caps->data);
- }
+ if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
+ detect_scsi_host_caps(&dev->def->caps->data) < 0)
+ return -1;
+
+ if (cap->type == VIR_NODE_DEV_CAP_NET &&
+ update_link(dev->def) < 0)
+ return -1;
+
cap = cap->next;
}
@@ -315,7 +331,8 @@ nodeDeviceGetXMLDesc(virNodeDevicePtr dev,
goto cleanup;
update_driver_name(obj);
- update_caps(obj);
+ if (update_caps(obj) < 0)
+ goto cleanup;
ret = virNodeDeviceDefFormat(obj->def);
diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c
index 8656b5d..e630bef 100644
--- a/src/node_device/node_device_hal.c
+++ b/src/node_device/node_device_hal.c
@@ -838,3 +838,12 @@ halNodeRegister(void)
return -1;
return virRegisterStateDriver(&halStateDriver);
}
+
+int
+halUpdateLink(virNodeDeviceDefPtr def ATTRIBUTE_UNUSED)
+{
+ /* Implement me */
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s"
+ _("This function is not implemented yet"));
+ return -1;
+}
diff --git a/src/node_device/node_device_hal.h b/src/node_device/node_device_hal.h
index 7226672..9fc92c0 100644
--- a/src/node_device/node_device_hal.h
+++ b/src/node_device/node_device_hal.h
@@ -22,4 +22,5 @@
#ifndef __VIR_NODE_DEVICE_HAL_H__
# define __VIR_NODE_DEVICE_HAL_H__
+int halUpdateLink(virNodeDeviceDefPtr def);
#endif /* __VIR_NODE_DEVICE_HAL_H__ */
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 9a951d9..2a5b586 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -631,6 +631,44 @@ static int udevProcessUSBInterface(struct udev_device *device,
}
+static int
+udevProcessNetworkInterfaceOperstate(struct udev_device *device,
+ virNodeDeviceDefPtr def)
+{
+ union _virNodeDevCapData *data = &def->caps->data;
+ const char *operstate;
+ const char *link_speed;
+ int ret = -1;
+
+ operstate = udev_device_get_sysattr_value(device, "operstate");
+ if ((data->net.lnk.state = virInterfaceStateTypeFromString(operstate)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse operstate: %s"),
+ operstate);
+ goto out;
+ }
+
+ link_speed = udev_device_get_sysattr_value(device, "speed");
+ if (link_speed) {
+ if (virStrToLong_ul(link_speed, NULL, 10, &data->net.lnk.speed) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse speed: %s"),
+ link_speed);
+ goto out;
+ }
+ /* Workaround broken kernel API. If the link is unplugged then
+ * depending on the NIC driver, link speed can be reported as -1.
+ * However, the value is printed out as unsigned integer instead of
+ * signed one. Terrifying but true. */
+ if ((int) data->net.lnk.speed == -1)
+ data->net.lnk.speed = 0;
+ }
+
+ ret = 0;
+ out:
+ return ret;
+}
+
static int udevProcessNetworkInterface(struct udev_device *device,
virNodeDeviceDefPtr def)
{
@@ -667,6 +705,9 @@ static int udevProcessNetworkInterface(struct udev_device *device,
goto out;
}
+ if (udevProcessNetworkInterfaceOperstate(device, def) < 0)
+ goto out;
+
ret = 0;
out:
@@ -1837,3 +1878,27 @@ int udevNodeRegister(void)
return virRegisterStateDriver(&udevStateDriver);
}
+
+int
+udevUpdateLink(virNodeDeviceDefPtr def)
+{
+ int ret = -1;
+ struct udev *udev = NULL;
+ struct udev_device *device = NULL;
+
+ udev = udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driverState));
+
+ device = udev_device_new_from_syspath(udev, def->sysfs_path);
+ if (!device) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to update link status on %s"),
+ def->sysfs_path);
+ goto cleanup;
+ }
+
+ ret = udevProcessNetworkInterfaceOperstate(device, def);
+ cleanup:
+ if (device)
+ udev_device_unref(device);
+ return ret;
+}
diff --git a/src/node_device/node_device_udev.h b/src/node_device/node_device_udev.h
index 47eeb1f..5e50d66 100644
--- a/src/node_device/node_device_udev.h
+++ b/src/node_device/node_device_udev.h
@@ -23,6 +23,8 @@
#include <libudev.h>
#include <stdint.h>
+#include "node_device_conf.h"
+
typedef struct _udevPrivate udevPrivate;
#define SYSFS_DATA_SIZE 4096
@@ -32,3 +34,5 @@ typedef struct _udevPrivate udevPrivate;
#define PROPERTY_FOUND 0
#define PROPERTY_MISSING 1
#define PROPERTY_ERROR -1
+
+int udevUpdateLink(virNodeDeviceDefPtr def);
--
2.0.0