mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 02:51:16 +00:00
SCSI HBA storage pool implementation (Dave Allan)
This commit is contained in:
parent
ecedb9cef3
commit
55ae53d971
15
ChangeLog
15
ChangeLog
@ -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
|
||||
|
11
configure.in
11
configure.in
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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,342 +170,32 @@ 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,
|
||||
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'"),
|
||||
sysfs_path);
|
||||
return -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 */
|
||||
virReportSystemError(conn, errno,
|
||||
_("Failed to find any LUNs for session '%s'"),
|
||||
session);
|
||||
return -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'"),
|
||||
_("Failed to get host number for iSCSI session "
|
||||
"with 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) {
|
||||
if (virStorageBackendSCSIFindLUs(conn, pool, host) < 0) {
|
||||
virReportSystemError(conn, errno,
|
||||
_("Failed to opendir sysfs path '%s'"),
|
||||
sysfs_path);
|
||||
_("Failed to find LUs on host %u"), host);
|
||||
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
510
src/storage_backend_scsi.c
Normal 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,
|
||||
};
|
43
src/storage_backend_scsi.h
Normal file
43
src/storage_backend_scsi.h
Normal 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__ */
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user