SCSI HBA storage pool implementation (Dave Allan)

This commit is contained in:
Daniel P. Berrange 2009-04-01 16:03:22 +00:00
parent ecedb9cef3
commit 55ae53d971
10 changed files with 633 additions and 325 deletions

View File

@ -1,3 +1,18 @@
Wed Apr 1 16:50:22 BST 2009 Daniel P. Berrange <berrange@redhat.com>
SCSI HBA storage pool implementation (Dave Allan)
* configure.in: Add flag for SCSI storage pool support
* po/POTFILES.in: Add storage_backend_scsi.c
* src/Makefile.am: Add new SCSI storage backend
* src/storage_backend.c: Support for SCSI pool type
* src/storage_backend_iscsi.c: Refactor to re-use logic from
SCSI pool backend
* src/storage_backend_scsi.c, src/storage_backend_scsi.h:
Generic pool for Linux SCSI HBAs (or things which look
like SCSI HBAs)
* src/storage_conf.c, src/storage_conf.h: Add logic for
SCSI storage pool XML parsing options
Wed Apr 1 11:40:22 BST 2009 Daniel P. Berrange <berrange@redhat.com>
* configure.in: Check for libsasl.so as well as libsasl2.so

View File

@ -792,6 +792,8 @@ AC_ARG_WITH([storage-lvm],
[ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check])
AC_ARG_WITH([storage-iscsi],
[ --with-storage-iscsi with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check])
AC_ARG_WITH([storage-scsi],
[ --with-storage-scsi with SCSI backend for the storage driver (on)],[],[with_storage_scsi=check])
AC_ARG_WITH([storage-disk],
[ --with-storage-disk with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check])
@ -801,6 +803,7 @@ if test "$with_libvirtd" = "no"; then
with_storage_fs=no
with_storage_lvm=no
with_storage_iscsi=no
with_storage_scsi=no
with_storage_disk=no
fi
if test "$with_storage_dir" = "yes" ; then
@ -929,6 +932,13 @@ if test "$with_storage_iscsi" = "yes" -o "$with_storage_iscsi" = "check"; then
fi
AM_CONDITIONAL([WITH_STORAGE_ISCSI], [test "$with_storage_iscsi" = "yes"])
if test "$with_storage_scsi" = "check"; then
with_storage_scsi=yes
AC_DEFINE_UNQUOTED([WITH_STORAGE_SCSI], 1,
[whether SCSI backend for storage driver is enabled])
AM_CONDITIONAL([WITH_STORAGE_SCSI], [test "$with_storage_scsi" = "yes"])
fi
LIBPARTED_CFLAGS=
@ -1365,6 +1375,7 @@ AC_MSG_NOTICE([ FS: $with_storage_fs])
AC_MSG_NOTICE([ NetFS: $with_storage_fs])
AC_MSG_NOTICE([ LVM: $with_storage_lvm])
AC_MSG_NOTICE([ iSCSI: $with_storage_iscsi])
AC_MSG_NOTICE([ SCSI: $with_storage_scsi])
AC_MSG_NOTICE([ Disk: $with_storage_disk])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])

View File

@ -30,6 +30,7 @@ src/storage_backend_disk.c
src/storage_backend_fs.c
src/storage_backend_iscsi.c
src/storage_backend_logical.c
src/storage_backend_scsi.c
src/storage_conf.c
src/storage_driver.c
src/test.c

View File

@ -159,6 +159,9 @@ STORAGE_DRIVER_LVM_SOURCES = \
STORAGE_DRIVER_ISCSI_SOURCES = \
storage_backend_iscsi.h storage_backend_iscsi.c
STORAGE_DRIVER_SCSI_SOURCES = \
storage_backend_scsi.h storage_backend_scsi.c
STORAGE_DRIVER_DISK_SOURCES = \
storage_backend_disk.h storage_backend_disk.c
@ -353,6 +356,10 @@ if WITH_STORAGE_ISCSI
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_ISCSI_SOURCES)
endif
if WITH_STORAGE_SCSI
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_SCSI_SOURCES)
endif
if WITH_STORAGE_DISK
libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
endif
@ -408,6 +415,7 @@ EXTRA_DIST += \
$(STORAGE_DRIVER_FS_SOURCES) \
$(STORAGE_DRIVER_LVM_SOURCES) \
$(STORAGE_DRIVER_ISCSI_SOURCES) \
$(STORAGE_DRIVER_SCSI_SOURCES) \
$(STORAGE_DRIVER_DISK_SOURCES) \
$(NODE_DEVICE_DRIVER_SOURCES) \
$(NODE_DEVICE_DRIVER_HAL_SOURCES) \

View File

@ -54,6 +54,9 @@
#if WITH_STORAGE_ISCSI
#include "storage_backend_iscsi.h"
#endif
#if WITH_STORAGE_SCSI
#include "storage_backend_scsi.h"
#endif
#if WITH_STORAGE_DISK
#include "storage_backend_disk.h"
#endif
@ -78,6 +81,9 @@ static virStorageBackendPtr backends[] = {
#if WITH_STORAGE_ISCSI
&virStorageBackendISCSI,
#endif
#if WITH_STORAGE_SCSI
&virStorageBackendSCSI,
#endif
#if WITH_STORAGE_DISK
&virStorageBackendDisk,
#endif

View File

@ -35,6 +35,7 @@
#include <dirent.h>
#include "virterror_internal.h"
#include "storage_backend_scsi.h"
#include "storage_backend_iscsi.h"
#include "util.h"
#include "memory.h"
@ -169,343 +170,33 @@ virStorageBackendISCSIConnection(virConnectPtr conn,
return 0;
}
static int
virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
unsigned int lun, const char *dev)
{
virStorageVolDefPtr vol;
int fd = -1;
char *devpath = NULL;
int opentries = 0;
if (VIR_ALLOC(vol) < 0) {
virReportOOMError(conn);
goto cleanup;
}
vol->type = VIR_STORAGE_VOL_BLOCK;
if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) {
virReportOOMError(conn);
goto cleanup;
}
if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
virReportOOMError(conn);
goto cleanup;
}
/* It can take a little while between logging into the ISCSI
* server and udev creating the /dev nodes, so if we get ENOENT
* we must retry a few times - they should eventually appear.
* We currently wait for upto 5 seconds. Is this good enough ?
* Perhaps not on a very heavily loaded system Any other
* options... ?
*/
reopen:
if ((fd = open(devpath, O_RDONLY)) < 0) {
opentries++;
if (errno == ENOENT && opentries < 50) {
usleep(100 * 1000);
goto reopen;
}
virReportSystemError(conn, errno,
_("cannot open '%s'"),
devpath);
goto cleanup;
}
/* Now figure out the stable path
*
* XXX this method is O(N) because it scans the pool target
* dir every time its run. Should figure out a more efficient
* way of doing this...
*/
if ((vol->target.path = virStorageBackendStablePath(conn,
pool,
devpath)) == NULL)
goto cleanup;
VIR_FREE(devpath);
if (virStorageBackendUpdateVolTargetInfoFD(conn,
&vol->target,
fd,
&vol->allocation,
&vol->capacity) < 0)
goto cleanup;
/* XXX use unique iSCSI id instead */
vol->key = strdup(vol->target.path);
if (vol->key == NULL) {
virReportOOMError(conn);
goto cleanup;
}
pool->def->capacity += vol->capacity;
pool->def->allocation += vol->allocation;
if (VIR_REALLOC_N(pool->volumes.objs,
pool->volumes.count+1) < 0) {
virReportOOMError(conn);
goto cleanup;
}
pool->volumes.objs[pool->volumes.count++] = vol;
close(fd);
return 0;
cleanup:
if (fd != -1) close(fd);
VIR_FREE(devpath);
virStorageVolDefFree(vol);
return -1;
}
static int notdotdir(const struct dirent *dir)
{
return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2));
}
/* Function to check if the type file in the given sysfs_path is a
* Direct-Access device (i.e. type 0). Return -1 on failure, 0 if not
* a Direct-Access device, and 1 if a Direct-Access device
*/
static int directAccessDevice(const char *sysfs_path)
{
char typestr[3];
char *gottype, *p;
FILE *typefile;
int type;
typefile = fopen(sysfs_path, "r");
if (typefile == NULL) {
/* there was no type file; that doesn't seem right */
return -1;
}
gottype = fgets(typestr, 3, typefile);
fclose(typefile);
if (gottype == NULL) {
/* we couldn't read the type file; have to give up */
return -1;
}
/* we don't actually care about p, but if you pass NULL and the last
* character is not \0, virStrToLong_i complains
*/
if (virStrToLong_i(typestr, &p, 10, &type) < 0) {
/* Hm, type wasn't an integer; seems strange */
return -1;
}
if (type != 0) {
/* saw a device other than Direct-Access */
return 0;
}
return 1;
}
static int
virStorageBackendISCSIFindLUNs(virConnectPtr conn,
virStoragePoolObjPtr pool,
const char *session)
virStorageBackendISCSIFindLUs(virConnectPtr conn,
virStoragePoolObjPtr pool,
const char *session)
{
char sysfs_path[PATH_MAX];
uint32_t host, bus, target, lun;
DIR *sysdir;
struct dirent *sys_dirent;
struct dirent **namelist;
int i, n, tries, retval, directaccess;
char *block, *scsidev, *block2;
retval = 0;
block = NULL;
scsidev = NULL;
int retval = 0;
uint32_t host;
snprintf(sysfs_path, PATH_MAX,
"/sys/class/iscsi_session/session%s/device", session);
sysdir = opendir(sysfs_path);
if (sysdir == NULL) {
if (virStorageBackendSCSIGetHostNumber(conn, sysfs_path, &host) < 0) {
virReportSystemError(conn, errno,
_("Failed to opendir sysfs path '%s'"),
_("Failed to get host number for iSCSI session "
"with path '%s'"),
sysfs_path);
return -1;
retval = -1;
}
while ((sys_dirent = readdir(sysdir))) {
/* double-negative, so we can use the same function for scandir below */
if (!notdotdir(sys_dirent))
continue;
if (STREQLEN(sys_dirent->d_name, "target", 6)) {
if (sscanf(sys_dirent->d_name, "target%u:%u:%u",
&host, &bus, &target) != 3) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to parse target from sysfs path %s/%s"),
sysfs_path, sys_dirent->d_name);
closedir(sysdir);
return -1;
}
break;
}
}
closedir(sysdir);
/* we now have the host, bus, and target; let's scan for LUNs */
snprintf(sysfs_path, PATH_MAX,
"/sys/class/iscsi_session/session%s/device/target%u:%u:%u",
session, host, bus, target);
n = scandir(sysfs_path, &namelist, notdotdir, versionsort);
if (n <= 0) {
/* we didn't find any reasonable entries; return failure */
if (virStorageBackendSCSIFindLUs(conn, pool, host) < 0) {
virReportSystemError(conn, errno,
_("Failed to find any LUNs for session '%s'"),
session);
return -1;
_("Failed to find LUs on host %u"), host);
retval = -1;
}
for (i=0; i<n; i++) {
block = NULL;
scsidev = NULL;
if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n",
&host, &bus, &target, &lun) != 4)
continue;
/* we found a LUN */
/* Note, however, that just finding a LUN doesn't mean it is
* actually useful to us. There are a few different types of
* LUNs, enumerated in the linux kernel in
* drivers/scsi/scsi.c:scsi_device_types[]. Luckily, these device
* types form part of the ABI between the kernel and userland, so
* are unlikely to change. For now, we ignore everything that isn't
* type 0; that is, a Direct-Access device
*/
snprintf(sysfs_path, PATH_MAX,
"/sys/bus/scsi/devices/%u:%u:%u:%u/type",
host, bus, target, lun);
directaccess = directAccessDevice(sysfs_path);
if (directaccess < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
host, bus, target, lun);
retval = -1;
goto namelist_cleanup;
}
else if (directaccess == 0) {
/* not a direct-access device; skip */
continue;
}
/* implicit else if (access == 1); Direct-Access device */
/* It might take some time for the
* /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda}
* link to show up; wait up to 5 seconds for it, then give up
*/
tries = 0;
while (block == NULL && tries < 50) {
snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u",
host, bus, target, lun);
sysdir = opendir(sysfs_path);
if (sysdir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir sysfs path '%s'"),
sysfs_path);
retval = -1;
goto namelist_cleanup;
}
while ((sys_dirent = readdir(sysdir))) {
if (!notdotdir(sys_dirent))
continue;
if (STREQLEN(sys_dirent->d_name, "block", 5)) {
block = strdup(sys_dirent->d_name);
break;
}
}
closedir(sysdir);
tries++;
if (block == NULL)
usleep(100 * 1000);
}
if (block == NULL) {
/* we couldn't find the device link for this device; fail */
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to find device link for lun %d"),
lun);
retval = -1;
goto namelist_cleanup;
}
if (strlen(block) == 5) {
/* OK, this is exactly "block"; must be new-style */
snprintf(sysfs_path, PATH_MAX,
"/sys/bus/scsi/devices/%u:%u:%u:%u/block",
host, bus, target, lun);
sysdir = opendir(sysfs_path);
if (sysdir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir sysfs path '%s'"),
sysfs_path);
retval = -1;
goto namelist_cleanup;
}
while ((sys_dirent = readdir(sysdir))) {
if (!notdotdir(sys_dirent))
continue;
scsidev = strdup(sys_dirent->d_name);
break;
}
closedir(sysdir);
}
else {
/* old-style; just parse out the sd */
block2 = strrchr(block, ':');
if (block2 == NULL) {
/* Hm, wasn't what we were expecting; have to give up */
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to parse block path %s"),
block);
retval = -1;
goto namelist_cleanup;
}
block2++;
scsidev = strdup(block2);
}
if (scsidev == NULL) {
virReportOOMError(conn);
retval = -1;
goto namelist_cleanup;
}
retval = virStorageBackendISCSINewLun(conn, pool, lun, scsidev);
if (retval < 0)
break;
VIR_FREE(scsidev);
VIR_FREE(block);
}
namelist_cleanup:
/* we call these VIR_FREE here to make sure we don't leak memory on
* error cases; in the success case, these are already freed but NULL,
* which should be fine
*/
VIR_FREE(scsidev);
VIR_FREE(block);
for (i=0; i<n; i++)
VIR_FREE(namelist[i]);
VIR_FREE(namelist);
return retval;
}
@ -615,13 +306,11 @@ virStorageBackendISCSIRefreshPool(virConnectPtr conn,
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
virStorageBackendWaitForDevices(conn);
if ((session = virStorageBackendISCSISession(conn, pool, 0)) == NULL)
goto cleanup;
if (virStorageBackendISCSIRescanLUNs(conn, pool, session) < 0)
goto cleanup;
if (virStorageBackendISCSIFindLUNs(conn, pool, session) < 0)
if (virStorageBackendISCSIFindLUs(conn, pool, session) < 0)
goto cleanup;
VIR_FREE(session);

510
src/storage_backend_scsi.c Normal file
View File

@ -0,0 +1,510 @@
/*
* storage_backend_scsi.c: storage backend for SCSI handling
*
* Copyright (C) 2007-2008 Red Hat, Inc.
* Copyright (C) 2007-2008 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange redhat com>
*/
#include <config.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include "virterror_internal.h"
#include "storage_backend_scsi.h"
#include "memory.h"
#include "logging.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
/* Function to check if the type file in the given sysfs_path is a
* Direct-Access device (i.e. type 0). Return -1 on failure, type of
* the device otherwise.
*/
static int
getDeviceType(virConnectPtr conn,
uint32_t host,
uint32_t bus,
uint32_t target,
uint32_t lun,
int *type)
{
char *type_path = NULL;
char typestr[3];
char *gottype, *p;
FILE *typefile;
int retval = 0;
if (virAsprintf(&type_path, "/sys/bus/scsi/devices/%u:%u:%u:%u/type",
host, bus, target, lun) < 0) {
virReportOOMError(conn);
goto out;
}
typefile = fopen(type_path, "r");
if (typefile == NULL) {
virReportSystemError(conn, errno,
_("Could not find typefile '%s'"),
type_path);
/* there was no type file; that doesn't seem right */
retval = -1;
goto out;
}
gottype = fgets(typestr, 3, typefile);
fclose(typefile);
if (gottype == NULL) {
virReportSystemError(conn, errno,
_("Could not read typefile '%s'"),
type_path);
/* we couldn't read the type file; have to give up */
retval = -1;
goto out;
}
/* we don't actually care about p, but if you pass NULL and the last
* character is not \0, virStrToLong_i complains
*/
if (virStrToLong_i(typestr, &p, 10, type) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Device type '%s' is not an integer"),
typestr);
/* Hm, type wasn't an integer; seems strange */
retval = -1;
goto out;
}
VIR_DEBUG(_("Device type is %d"), *type);
out:
VIR_FREE(type_path);
return retval;
}
static int
virStorageBackendSCSINewLun(virConnectPtr conn,
virStoragePoolObjPtr pool,
uint32_t host,
uint32_t bus,
uint32_t target,
uint32_t lun,
const char *dev)
{
virStorageVolDefPtr vol;
char *devpath = NULL;
int retval = 0;
if (VIR_ALLOC(vol) < 0) {
virReportOOMError(conn);
retval = -1;
goto out;
}
vol->type = VIR_STORAGE_VOL_BLOCK;
if (virAsprintf(&(vol->name), "%u.%u.%u.%u", host, bus, target, lun) < 0) {
virReportOOMError(conn);
retval = -1;
goto free_vol;
}
if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
virReportOOMError(conn);
retval = -1;
goto free_vol;
}
VIR_DEBUG(_("Trying to create volume for '%s'"), devpath);
/* Now figure out the stable path
*
* XXX this method is O(N) because it scans the pool target
* dir every time its run. Should figure out a more efficient
* way of doing this...
*/
if ((vol->target.path = virStorageBackendStablePath(conn,
pool,
devpath)) == NULL) {
retval = -1;
goto free_vol;
}
if (STREQLEN(devpath, vol->target.path, PATH_MAX) &&
!(STREQ(pool->def->target.path, "/dev") ||
STREQ(pool->def->target.path, "/dev/"))) {
VIR_DEBUG(_("No stable path found for '%s' in '%s'"),
devpath, pool->def->target.path);
retval = -1;
goto free_vol;
}
if (virStorageBackendUpdateVolTargetInfo(conn,
&vol->target,
&vol->allocation,
&vol->capacity) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to update volume for '%s'"),
devpath);
retval = -1;
goto free_vol;
}
/* XXX should use logical unit's UUID instead */
vol->key = strdup(vol->target.path);
if (vol->key == NULL) {
virReportOOMError(conn);
retval = -1;
goto free_vol;
}
pool->def->capacity += vol->capacity;
pool->def->allocation += vol->allocation;
if (VIR_REALLOC_N(pool->volumes.objs,
pool->volumes.count + 1) < 0) {
virReportOOMError(conn);
retval = -1;
goto free_vol;
}
pool->volumes.objs[pool->volumes.count++] = vol;
goto out;
free_vol:
virStorageVolDefFree(vol);
out:
VIR_FREE(devpath);
return retval;
}
static int
getNewStyleBlockDevice(virConnectPtr conn,
const char *lun_path,
const char *block_name ATTRIBUTE_UNUSED,
char **block_device)
{
char *block_path = NULL;
DIR *block_dir = NULL;
struct dirent *block_dirent = NULL;
int retval = 0;
if (virAsprintf(&block_path, "%s/block", lun_path) < 0) {
virReportOOMError(conn);
goto out;
}
VIR_DEBUG(_("Looking for block device in '%s'"), block_path);
block_dir = opendir(block_path);
if (block_dir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir sysfs path '%s'"),
block_path);
retval = -1;
goto out;
}
while ((block_dirent = readdir(block_dir))) {
if (STREQLEN(block_dirent->d_name, ".", 1)) {
continue;
}
*block_device = strdup(block_dirent->d_name);
VIR_DEBUG(_("Block device is '%s'"), *block_device);
break;
}
closedir(block_dir);
out:
VIR_FREE(block_path);
return retval;
}
static int
getOldStyleBlockDevice(virConnectPtr conn,
const char *lun_path ATTRIBUTE_UNUSED,
const char *block_name,
char **block_device)
{
char *blockp = NULL;
int retval = 0;
/* old-style; just parse out the sd */
blockp = strrchr(block_name, ':');
if (blockp == NULL) {
/* Hm, wasn't what we were expecting; have to give up */
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to parse block name %s"),
block_name);
retval = -1;
} else {
blockp++;
*block_device = strdup(blockp);
VIR_DEBUG(_("Block device is '%s'"), *block_device);
}
return retval;
}
static int
getBlockDevice(virConnectPtr conn,
uint32_t host,
uint32_t bus,
uint32_t target,
uint32_t lun,
char **block_device)
{
char *lun_path = NULL;
DIR *lun_dir = NULL;
struct dirent *lun_dirent = NULL;
int retval = 0;
if (virAsprintf(&lun_path, "/sys/bus/scsi/devices/%u:%u:%u:%u",
host, bus, target, lun) < 0) {
virReportOOMError(conn);
goto out;
}
lun_dir = opendir(lun_path);
if (lun_dir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir sysfs path '%s'"),
lun_path);
retval = -1;
goto out;
}
while ((lun_dirent = readdir(lun_dir))) {
if (STREQLEN(lun_dirent->d_name, "block", 5)) {
if (strlen(lun_dirent->d_name) == 5) {
retval = getNewStyleBlockDevice(conn,
lun_path,
lun_dirent->d_name,
block_device);
} else {
retval = getOldStyleBlockDevice(conn,
lun_path,
lun_dirent->d_name,
block_device);
}
break;
}
}
closedir(lun_dir);
out:
VIR_FREE(lun_path);
return retval;
}
static int
processLU(virConnectPtr conn,
virStoragePoolObjPtr pool,
uint32_t host,
uint32_t bus,
uint32_t target,
uint32_t lun)
{
char *type_path = NULL;
int retval = 0;
int device_type;
char *block_device = NULL;
VIR_DEBUG(_("Processing LU %u:%u:%u:%u"),
host, bus, target, lun);
if (getDeviceType(conn, host, bus, target, lun, &device_type) < 0) {
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
_("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
host, bus, target, lun);
retval = -1;
goto out;
}
/* We don't create volumes for devices other than disk and cdrom
* devices, but finding a device that isn't one of those types
* isn't an error, either. */
if (!(device_type == VIR_STORAGE_DEVICE_TYPE_DISK ||
device_type == VIR_STORAGE_DEVICE_TYPE_ROM))
{
retval = 0;
goto out;
}
VIR_DEBUG(_("%u:%u:%u:%u is a Direct-Access LUN"),
host, bus, target, lun);
if (getBlockDevice(conn, host, bus, target, lun, &block_device) < 0) {
goto out;
}
if (virStorageBackendSCSINewLun(conn, pool,
host, bus, target, lun,
block_device) < 0) {
VIR_DEBUG(_("Failed to create new storage volume for %u:%u:%u:%u"),
host, bus, target, lun);
retval = -1;
goto out;
}
VIR_DEBUG(_("Created new storage volume for %u:%u:%u:%u successfully"),
host, bus, target, lun);
VIR_FREE(type_path);
out:
return retval;
}
int
virStorageBackendSCSIFindLUs(virConnectPtr conn,
virStoragePoolObjPtr pool,
uint32_t scanhost)
{
int retval = 0;
uint32_t bus, target, lun;
char *device_path = NULL;
DIR *devicedir = NULL;
struct dirent *lun_dirent = NULL;
char devicepattern[64];
VIR_DEBUG(_("Discovering LUs on host %u"), scanhost);
virStorageBackendWaitForDevices(conn);
if (virAsprintf(&device_path, "/sys/bus/scsi/devices") < 0) {
virReportOOMError(conn);
goto out;
}
devicedir = opendir(device_path);
if (devicedir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir path '%s'"), device_path);
retval = -1;
goto out;
}
snprintf(devicepattern, sizeof(devicepattern), "%u:%%u:%%u:%%u\n", scanhost);
while ((lun_dirent = readdir(devicedir))) {
if (sscanf(lun_dirent->d_name, devicepattern,
&bus, &target, &lun) != 3) {
continue;
}
VIR_DEBUG(_("Found LU '%s'"), lun_dirent->d_name);
processLU(conn, pool, scanhost, bus, target, lun);
}
closedir(devicedir);
out:
VIR_FREE(device_path);
return retval;
}
int
virStorageBackendSCSIGetHostNumber(virConnectPtr conn,
const char *sysfs_path,
uint32_t *host)
{
int retval = 0;
DIR *sysdir = NULL;
struct dirent *dirent = NULL;
VIR_DEBUG(_("Finding host number from '%s'"), sysfs_path);
virStorageBackendWaitForDevices(conn);
sysdir = opendir(sysfs_path);
if (sysdir == NULL) {
virReportSystemError(conn, errno,
_("Failed to opendir path '%s'"), sysfs_path);
retval = -1;
goto out;
}
while ((dirent = readdir(sysdir))) {
if (STREQLEN(dirent->d_name, "target", strlen("target"))) {
if (sscanf(dirent->d_name,
"target%u:", host) != 1) {
VIR_DEBUG(_("Failed to parse target '%s'"), dirent->d_name);
retval = -1;
break;
}
}
}
closedir(sysdir);
out:
return retval;
}
static int
virStorageBackendSCSIRefreshPool(virConnectPtr conn,
virStoragePoolObjPtr pool)
{
int retval = 0;
uint32_t host;
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) {
VIR_DEBUG(_("Failed to get host number from '%s'"), pool->def->source.adapter);
retval = -1;
goto out;
}
VIR_DEBUG(_("Scanning host%u"), host);
virStorageBackendSCSIFindLUs(conn, pool, host);
out:
return retval;
}
virStorageBackend virStorageBackendSCSI = {
.type = VIR_STORAGE_POOL_SCSI,
.refreshPool = virStorageBackendSCSIRefreshPool,
};

View File

@ -0,0 +1,43 @@
/*
* storage_backend_scsi.h: storage backend for SCSI handling
*
* Copyright (C) 2007-2008 Red Hat, Inc.
* Copyright (C) 2007-2008 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange redhat com>
*/
#ifndef __VIR_STORAGE_BACKEND_SCSI_H__
#define __VIR_STORAGE_BACKEND_SCSI_H__
#include "storage_backend.h"
#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
extern virStorageBackend virStorageBackendSCSI;
int
virStorageBackendSCSIGetHostNumber(virConnectPtr conn,
const char *sysfs_path,
uint32_t *host);
int
virStorageBackendSCSIFindLUs(virConnectPtr conn,
virStoragePoolObjPtr pool,
uint32_t scanhost);
#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */

View File

@ -187,6 +187,14 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
{ .poolType = VIR_STORAGE_POOL_SCSI,
.poolOptions = {
.flags = (VIR_STORAGE_POOL_SOURCE_ADAPTER),
},
.volOptions = {
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
{ .poolType = VIR_STORAGE_POOL_DISK,
.poolOptions = {
.flags = (VIR_STORAGE_POOL_SOURCE_DEVICE),
@ -269,6 +277,7 @@ virStoragePoolSourceFree(virStoragePoolSourcePtr source) {
VIR_FREE(source->devices);
VIR_FREE(source->dir);
VIR_FREE(source->name);
VIR_FREE(source->adapter);
if (source->authType == VIR_STORAGE_POOL_AUTH_CHAP) {
VIR_FREE(source->auth.chap.login);
@ -573,6 +582,15 @@ virStoragePoolDefParseDoc(virConnectPtr conn,
}
}
if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) {
if ((ret->source.adapter = virXPathString(conn,
"string(/pool/source/adapter/@name)",
ctxt)) == NULL) {
virStorageReportError(conn, VIR_ERR_XML_ERROR,
"%s", _("missing storage pool source adapter name"));
goto cleanup;
}
}
authType = virXPathString(conn, "string(/pool/source/auth/@type)", ctxt);
if (authType == NULL) {

View File

@ -117,6 +117,13 @@ enum virStoragePoolType {
VIR_ENUM_DECL(virStoragePool)
enum virStoragePoolDeviceType {
VIR_STORAGE_DEVICE_TYPE_DISK = 0x00,
VIR_STORAGE_DEVICE_TYPE_ROM = 0x05,
VIR_STORAGE_DEVICE_TYPE_LAST,
};
enum virStoragePoolAuthType {
VIR_STORAGE_POOL_AUTH_NONE,