---
src/node_device.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++
src/node_device.h | 14 ++
src/node_device_conf.c | 106 ++++++++++++++-
src/node_device_conf.h | 16 ++
src/node_device_hal.c | 134 ++++++++++++++++++
src/storage_backend.c | 24 +---
src/virsh.c | 53 +++++++
7 files changed, 678 insertions(+), 24 deletions(-)
diff --git a/src/node_device.c b/src/node_device.c
index b84729f..25d3251 100644
--- a/src/node_device.c
+++ b/src/node_device.c
@@ -25,6 +25,8 @@
#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
#include "virterror_internal.h"
#include "datatypes.h"
@@ -133,6 +135,53 @@ cleanup:
return ret;
}
+
+/* Caller must hold the driver lock. */
+static virNodeDevicePtr
+nodeDeviceLookupByWWN(virConnectPtr conn,
+ const char *wwnn,
+ const char *wwpn)
+{
+ unsigned int i, found = 0;
+ virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+ virNodeDeviceObjListPtr devs = &driver->devs;
+ virNodeDevCapsDefPtr cap = NULL;
+ virNodeDeviceObjPtr obj = NULL;
+ virNodeDevicePtr dev = NULL;
+
+ for (i = 0; i < devs->count; i++) {
+
+ obj = devs->objs[i];
+ virNodeDeviceObjLock(obj);
+ cap = obj->def->caps;
+
+ while (cap) {
+
+ if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) {
+ if (cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)
{
+ if (STREQ(cap->data.scsi_host.wwnn, wwnn) &&
+ STREQ(cap->data.scsi_host.wwpn, wwpn)) {
+ found = 1;
+ goto out;
+ }
+ }
+ }
+ cap = cap->next;
+ }
+
+ virNodeDeviceObjUnlock(obj);
+ }
+
+out:
+ if (found) {
+ dev = virGetNodeDevice(conn, obj->def->name);
+ virNodeDeviceObjUnlock(obj);
+ }
+
+ return dev;
+}
+
+
static char *nodeDeviceDumpXML(virNodeDevicePtr dev,
unsigned int flags ATTRIBUTE_UNUSED)
{
@@ -258,6 +307,310 @@ cleanup:
}
+static int
+nodeDeviceVportCreateDelete(virConnectPtr conn,
+ const int parent_host,
+ const char *wwpn,
+ const char *wwnn,
+ int operation)
+{
+ int fd = -1;
+ int retval = 0;
+ char *operation_path;
+ const char *operation_file;
+ char *vport_name;
+ size_t towrite = 0;
+ unsigned int written = 0;
+
+ switch (operation) {
+ case VPORT_CREATE:
+ operation_file = LINUX_SYSFS_VPORT_CREATE_POSTFIX;
+ break;
+ case VPORT_DELETE:
+ operation_file = LINUX_SYSFS_VPORT_DELETE_POSTFIX;
+ break;
+ default:
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Invalid vport operation (%d)"),
operation);
+ retval = -1;
+ goto no_unwind;
+ break;
+ }
+
+ if (virAsprintf(&operation_path,
+ "%shost%d%s",
+ LINUX_SYSFS_FC_HOST_PREFIX,
+ parent_host,
+ operation_file) < 0) {
+
+ virReportOOMError(conn);
+ retval = -1;
+ goto no_unwind;
+ }
+
+ VIR_DEBUG(_("Vport operation path is '%s'"), operation_path);
+
+ fd = open(operation_path, O_WRONLY);
+
+ if (fd < 0) {
+ virReportSystemError(conn, errno,
+ _("Could not open '%s' for vport
operation"),
+ operation_path);
+ retval = -1;
+ goto free_path;
+ }
+
+ if (virAsprintf(&vport_name,
+ "%s:%s",
+ wwpn,
+ wwnn) < 0) {
+
+ virReportOOMError(conn);
+ retval = -1;
+ goto close_fd;
+ }
+
+ towrite = strlen(vport_name);
+ written = safewrite(fd, vport_name, towrite);
+ if (written != towrite) {
+ virReportSystemError(conn, errno,
+ _("Write of '%s' to '%s' during "
+ "vport create/delete failed "
+ "(towrite: %lu written: %d)"),
+ vport_name, operation_path,
+ towrite, written);
+ retval = -1;
+ }
+
+ VIR_FREE(vport_name);
+close_fd:
+ close(fd);
+free_path:
+ VIR_FREE(operation_path);
+no_unwind:
+ VIR_DEBUG("%s", _("Vport operation complete"));
+ return retval;
+}
+
+
+static int
+get_wwns(virConnectPtr conn,
+ virNodeDeviceDefPtr def,
+ char **wwnn,
+ char **wwpn)
+{
+ virNodeDevCapsDefPtr cap = NULL;
+ int ret = 0;
+
+ cap = def->caps;
+ while (cap != NULL) {
+ if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
+ cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
+ *wwnn = cap->data.scsi_host.wwnn;
+ *wwpn = cap->data.scsi_host.wwnn;
+ break;
+ }
+
+ cap = cap->next;
+ }
+
+ if (cap == NULL) {
+ /* XXX This error code is wrong--it results in errors of the form:
+ "error: invalid node device pointer in Device foo is not a fibre channel
HBA"
+ */
+ virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
+ _("Device %s is not a fibre channel HBA"),
+ def->name);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static int
+get_parent_host(virConnectPtr conn,
+ virDeviceMonitorStatePtr driver,
+ virNodeDeviceDefPtr def,
+ int *parent_host)
+{
+ virNodeDeviceObjPtr parent = NULL;
+ virNodeDevCapsDefPtr cap = NULL;
+ int ret = 0;
+
+ parent = virNodeDeviceFindByName(&driver->devs, def->parent);
+ if (parent == NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
+ _("Could not find parent device for
'%s'"),
+ def->name);
+ ret = -1;
+ goto out;
+ }
+
+ cap = parent->def->caps;
+ while (cap != NULL) {
+ if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) {
+ *parent_host = cap->data.scsi_host.host;
+ break;
+ }
+
+ cap = cap->next;
+ }
+
+ if (cap == NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE,
+ _("Device %s is not a SCSI host"),
+ parent->def->name);
+ ret = -1;
+ }
+
+out:
+ if (parent != NULL) {
+ virNodeDeviceObjUnlock(parent);
+ }
+
+ return ret;
+}
+
+
+static virNodeDevicePtr
+find_new_device(virConnectPtr conn, const char *wwnn, const char *wwpn)
+{
+ virNodeDevicePtr dev = NULL;
+ virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+ time_t start = 0, now = 0;
+
+ /* When large numbers of devices are present on the host, it's
+ * possible for udev not to realize that it has work to do before
+ * we get here. We thus keep trying to find the new device we
+ * just created for up to LINUX_NEW_DEVICE_WAIT_TIME. Note that
+ * udev's default settle time is 180 seconds, so once udev
+ * realizes that it has work to do, it might take that long for
+ * the udev wait to return. Thus the total maximum time for this
+ * function to return is the udev settle time plus
+ * LINUX_NEW_DEVICE_WAIT_TIME.
+ *
+ * This whole area is generally racy, but if we retry the udev
+ * wait for LINUX_NEW_DEVICE_WAIT_TIME seconds and there's still
+ * no device, it's probably safe to assume it's not going to
+ * appear. */
+ start = time(NULL);
+ if (start == (time_t)-1) {
+ virNodeDeviceReportError(dev->conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not get current
time"));
+ /* Set start time to the epoch so we try find the device
+ * once. */
+ start = 0;
+ }
+
+ while (dev == NULL && now - start < LINUX_NEW_DEVICE_WAIT_TIME) {
+ /* We can't hold the driver lock while we wait because the
+ wait for devices call takes it. It's safe to drop the lock
+ because we're done with the driver structure at this point
+ anyway. We take it again when we look to see what, if
+ anything, was created. */
+ nodeDeviceUnlock(driver);
+ virNodeDeviceWaitForDevices(conn);
+ nodeDeviceLock(driver);
+
+ dev = nodeDeviceLookupByWWN(conn, wwnn, wwpn);
+
+ if (dev == NULL) {
+ sleep(5);
+ now = time(NULL);
+ if (now == (time_t)-1) {
+ virNodeDeviceReportError(dev->conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not get current
time"));
+ break;
+ }
+ }
+ }
+
+ return dev;
+}
+
+static virNodeDevicePtr
+nodeDeviceCreateXML(virConnectPtr conn,
+ const char *xmlDesc,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ virDeviceMonitorStatePtr driver = conn->devMonPrivateData;
+ virNodeDeviceDefPtr def = NULL;
+ char *wwnn = NULL, *wwpn = NULL;
+ int parent_host = -1;
+ virNodeDevicePtr dev = NULL;
+
+ nodeDeviceLock(driver);
+
+ def = virNodeDeviceDefParseString(conn, xmlDesc);
+ if (def == NULL) {
+ goto cleanup;
+ }
+
+ if (get_wwns(conn, def, &wwnn, &wwpn) == -1) {
+ goto cleanup;
+ }
+
+ if (get_parent_host(conn, driver, def, &parent_host) == -1) {
+ goto cleanup;
+ }
+
+ if (nodeDeviceVportCreateDelete(conn,
+ parent_host,
+ wwpn,
+ wwnn,
+ VPORT_CREATE) == -1) {
+ goto cleanup;
+ }
+
+ dev = find_new_device(conn, wwnn, wwpn);
+ /* We don't check the return value, because one way or another,
+ * we're returning what we get... */
+
+ if (dev == NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_NO_NODE_DEVICE, NULL);
+ }
+
+cleanup:
+ nodeDeviceUnlock(driver);
+ virNodeDeviceDefFree(def);
+ return dev;
+}
+
+
+static int
+nodeDeviceDestroy(virNodeDevicePtr dev ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+
+#if defined(UDEVADM) || defined(UDEVSETTLE)
+void virNodeDeviceWaitForDevices(virConnectPtr conn)
+{
+#ifdef UDEVADM
+ const char *const settleprog[] = { UDEVADM, "settle", NULL };
+#else
+ const char *const settleprog[] = { UDEVSETTLE, NULL };
+#endif
+ int exitstatus;
+
+ if (access(settleprog[0], X_OK) != 0)
+ return;
+
+ /*
+ * NOTE: we ignore errors here; this is just to make sure that any device
+ * nodes that are being created finish before we try to scan them.
+ * If this fails for any reason, we still have the backup of polling for
+ * 5 seconds for device nodes.
+ */
+ virRun(conn, settleprog, &exitstatus);
+}
+#else
+void virNodeDeviceWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
+#endif
+
+
void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
{
driver->numOfDevices = nodeNumOfDevices;
@@ -267,6 +620,8 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr driver)
driver->deviceGetParent = nodeDeviceGetParent;
driver->deviceNumOfCaps = nodeDeviceNumOfCaps;
driver->deviceListCaps = nodeDeviceListCaps;
+ driver->deviceCreateXML = nodeDeviceCreateXML;
+ driver->deviceDestroy = nodeDeviceDestroy;
}
diff --git a/src/node_device.h b/src/node_device.h
index 9496120..59cd5f5 100644
--- a/src/node_device.h
+++ b/src/node_device.h
@@ -28,6 +28,18 @@
#include "driver.h"
#include "node_device_conf.h"
+
+#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
+#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
+#define LINUX_SYSFS_FC_HOST_PREFIX "/sys/class/fc_host/"
+
+#define VPORT_CREATE 0
+#define VPORT_DELETE 1
+#define LINUX_SYSFS_VPORT_CREATE_POSTFIX "/vport_create"
+#define LINUX_SYSFS_VPORT_DELETE_POSTFIX "/vport_delete"
+
+#define LINUX_NEW_DEVICE_WAIT_TIME 60
+
#ifdef HAVE_HAL
int halNodeRegister(void);
#endif
@@ -42,4 +54,6 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr mon);
int nodedevRegister(void);
+void virNodeDeviceWaitForDevices(virConnectPtr conn);
+
#endif /* __VIR_NODE_DEVICE_H__ */
diff --git a/src/node_device_conf.c b/src/node_device_conf.c
index 6e04112..6c74551 100644
--- a/src/node_device_conf.c
+++ b/src/node_device_conf.c
@@ -53,9 +53,34 @@ VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
"80203",
"80211")
+VIR_ENUM_IMPL(virNodeDevHBACap, VIR_NODE_DEV_CAP_HBA_LAST,
+ "fc_host",
+ "vport_ops")
#define virNodeDeviceLog(msg...) fprintf(stderr, msg)
+static int
+virNodeDevCapsDefParseString(virConnectPtr conn,
+ const char *xpath,
+ xmlXPathContextPtr ctxt,
+ char **string,
+ virNodeDeviceDefPtr def,
+ const char *missing_error_fmt)
+{
+ char *s;
+
+ s = virXPathString(conn, xpath, ctxt);
+ if (s == NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ missing_error_fmt,
+ def->name);
+ return -1;
+ }
+
+ *string = s;
+ return 0;
+}
+
virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
const char *name)
{
@@ -302,6 +327,18 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
case VIR_NODE_DEV_CAP_SCSI_HOST:
virBufferVSprintf(&buf, " <host>%d</host>\n",
data->scsi_host.host);
+ if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
+ virBufferAddLit(&buf, " <capability
type='fc_host'>\n");
+ virBufferVSprintf(&buf,
+ " <wwnn>%s</wwnn>\n",
data->scsi_host.wwnn);
+ virBufferVSprintf(&buf,
+ " <wwpn>%s</wwpn>\n",
data->scsi_host.wwpn);
+ virBufferAddLit(&buf, " </capability>\n");
+ }
+ if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) {
+ virBufferAddLit(&buf, " <capability
type='vport_ops' />\n");
+ }
+
break;
case VIR_NODE_DEV_CAP_SCSI:
virBufferVSprintf(&buf, " <host>%d</host>\n",
data->scsi.host);
@@ -563,8 +600,9 @@ virNodeDevCapScsiHostParseXML(virConnectPtr conn,
xmlNodePtr node,
union _virNodeDevCapData *data)
{
- xmlNodePtr orignode;
- int ret = -1;
+ xmlNodePtr orignode, *nodes = NULL;
+ int ret = -1, n = 0, i;
+ char *type = NULL;
orignode = ctxt->node;
ctxt->node = node;
@@ -572,15 +610,77 @@ virNodeDevCapScsiHostParseXML(virConnectPtr conn,
if (virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt,
&data->scsi_host.host, def,
_("no SCSI host ID supplied for
'%s'"),
- _("invalid SCSI host ID supplied for
'%s'")) < 0)
+ _("invalid SCSI host ID supplied for
'%s'")) < 0) {
+ goto out;
+ }
+
+ if ((n = virXPathNodeSet(conn, "./capability", ctxt, &nodes)) < 0)
{
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("error parsing SCSI host capabilities for
'%s'"),
+ def->name);
goto out;
+ }
+
+ for (i = 0 ; i < n ; i++) {
+ type = virXMLPropString(nodes[i], "type");
+
+ if (!type) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("missing SCSI host capability type for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ if (STREQ(type, "vport_ops")) {
+
+ data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
+
+ } else if (STREQ(type, "fc_host")) {
+
+ xmlNodePtr orignode2;
+
+ data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
+
+ orignode2 = ctxt->node;
+ ctxt->node = nodes[i];
+
+ if (virNodeDevCapsDefParseString(conn, "string(./wwnn[1])",
+ ctxt,
+ &data->scsi_host.wwnn,
+ def,
+ _("no WWNN supplied for
'%s'")) < 0) {
+ goto out;
+ }
+
+ if (virNodeDevCapsDefParseString(conn, "string(./wwpn[1])",
+ ctxt,
+ &data->scsi_host.wwpn,
+ def,
+ _("no WWPN supplied for
'%s'")) < 0) {
+ goto out;
+ }
+
+ ctxt->node = orignode2;
+
+ } else {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown SCSI host capability type
'%s' for '%s'"),
+ type, def->name);
+ goto out;
+ }
+
+ VIR_FREE(type);
+ }
ret = 0;
+
out:
+ VIR_FREE(type);
ctxt->node = orignode;
return ret;
}
+
static int
virNodeDevCapNetParseXML(virConnectPtr conn,
xmlXPathContextPtr ctxt,
diff --git a/src/node_device_conf.h b/src/node_device_conf.h
index 26e5558..8233a91 100644
--- a/src/node_device_conf.h
+++ b/src/node_device_conf.h
@@ -48,8 +48,16 @@ enum virNodeDevNetCapType {
VIR_NODE_DEV_CAP_NET_LAST
};
+enum virNodeDevHBACapType {
+ /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */
+ VIR_NODE_DEV_CAP_HBA_FC_HOST, /* fibre channel HBA */
+ VIR_NODE_DEV_CAP_HBA_VPORT_OPS, /* capable of vport operations */
+ VIR_NODE_DEV_CAP_HBA_LAST
+};
+
VIR_ENUM_DECL(virNodeDevCap)
VIR_ENUM_DECL(virNodeDevNetCap)
+VIR_ENUM_DECL(virNodeDevHBACap)
enum virNodeDevStorageCapFlags {
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE = (1 << 0),
@@ -57,6 +65,11 @@ enum virNodeDevStorageCapFlags {
VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE = (1 << 2),
};
+enum virNodeDevScsiHostCapFlags {
+ VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST = (1 << 0),
+ VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS = (1 << 1),
+};
+
typedef struct _virNodeDevCapsDef virNodeDevCapsDef;
typedef virNodeDevCapsDef *virNodeDevCapsDefPtr;
struct _virNodeDevCapsDef {
@@ -108,6 +121,9 @@ struct _virNodeDevCapsDef {
} net;
struct {
unsigned host;
+ char *wwnn;
+ char *wwpn;
+ unsigned flags;
} scsi_host;
struct {
unsigned host;
diff --git a/src/node_device_hal.c b/src/node_device_hal.c
index b214f60..5e54044 100644
--- a/src/node_device_hal.c
+++ b/src/node_device_hal.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <libhal.h>
+#include <fcntl.h>
#include "node_device_conf.h"
#include "virterror_internal.h"
@@ -37,6 +38,8 @@
#include "logging.h"
#include "node_device.h"
+#define VIR_FROM_THIS VIR_FROM_NODEDEV
+
/*
* Host device enumeration (HAL implementation)
*/
@@ -211,10 +214,141 @@ static int gather_net_cap(LibHalContext *ctx, const char *udi,
}
+static int check_fc_host(union _virNodeDevCapData *d)
+{
+ char *sysfs_path = NULL;
+ char *wwnn_path = NULL;
+ char *wwpn_path = NULL;
+ char *p = NULL;
+ int fd = -1;
+ char buf[64];
+ struct stat st;
+
+ VIR_DEBUG(_("Checking if host%d is an FC HBA"), d->scsi_host.host);
+
+ if (virAsprintf(&sysfs_path, "%s/host%d",
+ LINUX_SYSFS_FC_HOST_PREFIX,
+ d->scsi_host.host) < 0) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if (stat(sysfs_path, &st) != 0) {
+ /* Not an FC HBA */
+ goto out;
+ }
+
+ d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
+
+ if (virAsprintf(&wwnn_path, "%s/node_name",
+ sysfs_path) < 0) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if ((fd = open(wwnn_path, O_RDONLY)) < 0) {
+ goto out;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ if (saferead(fd, buf, sizeof(buf)) < 0) {
+ goto out;
+ }
+
+ close(fd);
+
+ p = strstr(buf, "0x");
+ if (p != NULL) {
+ p += strlen("0x");
+ } else {
+ p = buf;
+ }
+
+ d->scsi_host.wwnn = strndup(p, sizeof(buf));
+ if (d->scsi_host.wwnn == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ p = strchr(d->scsi_host.wwnn, '\n');
+ if (p != NULL) {
+ *p = '\0';
+ }
+
+ if (virAsprintf(&wwpn_path, "%s/port_name",
+ sysfs_path) < 0) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if ((fd = open(wwpn_path, O_RDONLY)) < 0) {
+ goto out;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ if (saferead(fd, buf, sizeof(buf)) < 0) {
+ goto out;
+ }
+
+ close(fd);
+
+ p = strstr(buf, "0x");
+ if (p != NULL) {
+ p += strlen("0x");
+ } else {
+ p = buf;
+ }
+
+ d->scsi_host.wwpn = strndup(p, sizeof(buf));
+ if (d->scsi_host.wwpn == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ p = strchr(d->scsi_host.wwpn, '\n');
+ if (p != NULL) {
+ *p = '\0';
+ }
+
+out:
+ VIR_FREE(sysfs_path);
+ VIR_FREE(wwnn_path);
+ VIR_FREE(wwpn_path);
+ return 0;
+}
+
+
+static int check_vport_capable(union _virNodeDevCapData *d)
+{
+ char *sysfs_path = NULL;
+ struct stat st;
+
+ if (virAsprintf(&sysfs_path, "%s/host%d/vport_create",
+ LINUX_SYSFS_FC_HOST_PREFIX,
+ d->scsi_host.host) < 0) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ if (stat(sysfs_path, &st) != 0) {
+ /* Not a vport capable HBA */
+ goto out;
+ }
+
+ d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
+
+out:
+ VIR_FREE(sysfs_path);
+ return 0;
+}
+
+
static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi,
union _virNodeDevCapData *d)
{
(void)get_int_prop(ctx, udi, "scsi_host.host", (int
*)&d->scsi_host.host);
+ (void)check_fc_host(d);
+ (void)check_vport_capable(d);
return 0;
}
diff --git a/src/storage_backend.c b/src/storage_backend.c
index b154140..74759cf 100644
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -46,6 +46,7 @@
#include "virterror_internal.h"
#include "util.h"
#include "memory.h"
+#include "node_device.h"
#include "storage_backend.h"
@@ -245,30 +246,11 @@ virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
return 0;
}
-#if defined(UDEVADM) || defined(UDEVSETTLE)
void virStorageBackendWaitForDevices(virConnectPtr conn)
{
-#ifdef UDEVADM
- const char *const settleprog[] = { UDEVADM, "settle", NULL };
-#else
- const char *const settleprog[] = { UDEVSETTLE, NULL };
-#endif
- int exitstatus;
-
- if (access(settleprog[0], X_OK) != 0)
- return;
-
- /*
- * NOTE: we ignore errors here; this is just to make sure that any device
- * nodes that are being created finish before we try to scan them.
- * If this fails for any reason, we still have the backup of polling for
- * 5 seconds for device nodes.
- */
- virRun(conn, settleprog, &exitstatus);
+ virNodeDeviceWaitForDevices(conn);
+ return;
}
-#else
-void virStorageBackendWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {}
-#endif
/*
* Given a volume path directly in /dev/XXX, iterate over the
diff --git a/src/virsh.c b/src/virsh.c
index 2e41c02..1a094eb 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -2962,6 +2962,58 @@ cmdPoolCreate(vshControl *ctl, const vshCmd *cmd)
/*
+ * "nodedev-create" command
+ */
+static const vshCmdInfo info_node_device_create[] = {
+ {"help", gettext_noop("create a device defined "
+ "by an XML file on the node")},
+ {"desc", gettext_noop("Create a device on the node. Note that this
"
+ "command creates devices on the physical host "
+ "that can then be assigned to a virtual machine.")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_node_device_create[] = {
+ {"file", VSH_OT_DATA, VSH_OFLAG_REQ,
+ gettext_noop("file containing an XML description of the device")},
+ {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNodeDeviceCreate(vshControl *ctl, const vshCmd *cmd)
+{
+ virNodeDevicePtr dev = NULL;
+ char *from;
+ int found;
+ int ret = TRUE;
+ char *buffer;
+
+ if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+ return FALSE;
+
+ from = vshCommandOptString(cmd, "file", &found);
+ if (!found)
+ return FALSE;
+
+ if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0)
+ return FALSE;
+
+ dev = virNodeDeviceCreateXML(ctl->conn, buffer, 0);
+ free (buffer);
+
+ if (dev != NULL) {
+ vshPrint(ctl, _("Node device %s created from %s\n"),
+ virNodeDeviceGetName(dev), from);
+ } else {
+ vshError(ctl, FALSE, _("Failed to create node device from %s"), from);
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+
+/*
* XML Building helper for pool-define-as and pool-create-as
*/
static const vshCmdOptDef opts_pool_X_as[] = {
@@ -5895,6 +5947,7 @@ static const vshCmdDef commands[] = {
{"nodedev-dettach", cmdNodeDeviceDettach, opts_node_device_dettach,
info_node_device_dettach},
{"nodedev-reattach", cmdNodeDeviceReAttach, opts_node_device_reattach,
info_node_device_reattach},
{"nodedev-reset", cmdNodeDeviceReset, opts_node_device_reset,
info_node_device_reset},
+ {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create,
info_node_device_create},
{"pool-autostart", cmdPoolAutostart, opts_pool_autostart,
info_pool_autostart},
{"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build},
--
1.6.0.6