2009-04-01 16:03:22 +00:00
|
|
|
/*
|
|
|
|
* storage_backend_scsi.c: storage backend for SCSI handling
|
|
|
|
*
|
2014-02-12 10:36:06 +00:00
|
|
|
* Copyright (C) 2007-2008, 2013-2014 Red Hat, Inc.
|
2009-04-01 16:03:22 +00:00
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-04-01 16:03:22 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange redhat com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2009-04-01 16:03:22 +00:00
|
|
|
#include "storage_backend_scsi.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("storage.storage_backend_scsi");
|
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
/* 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
|
2010-02-10 11:42:56 +00:00
|
|
|
getDeviceType(uint32_t host,
|
2009-04-01 16:03:22 +00:00
|
|
|
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",
|
2013-07-04 10:16:29 +00:00
|
|
|
host, bus, target, lun) < 0)
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
typefile = fopen(type_path, "r");
|
|
|
|
if (typefile == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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);
|
2010-11-17 02:13:29 +00:00
|
|
|
VIR_FORCE_FCLOSE(typefile);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
if (gottype == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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) {
|
2012-07-18 11:38:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Device type '%s' is not an integer"),
|
|
|
|
typestr);
|
2009-04-01 16:03:22 +00:00
|
|
|
/* Hm, type wasn't an integer; seems strange */
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Device type is %d", *type);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
VIR_FREE(type_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
static char *
|
|
|
|
virStorageBackendSCSISerial(const char *dev)
|
|
|
|
{
|
|
|
|
char *serial = NULL;
|
2012-09-20 14:24:47 +00:00
|
|
|
#ifdef WITH_UDEV
|
2010-11-12 15:49:40 +00:00
|
|
|
virCommandPtr cmd = virCommandNewArgList(
|
|
|
|
"/lib/udev/scsi_id",
|
|
|
|
"--replace-whitespace",
|
|
|
|
"--whitelisted",
|
|
|
|
"--device", dev,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Run the program and capture its output */
|
|
|
|
virCommandSetOutputBuffer(cmd, &serial);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (serial && STRNEQ(serial, "")) {
|
|
|
|
char *nl = strchr(serial, '\n');
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
|
|
|
} else {
|
|
|
|
VIR_FREE(serial);
|
2013-05-03 12:49:08 +00:00
|
|
|
ignore_value(VIR_STRDUP(serial, dev));
|
2010-11-12 15:49:40 +00:00
|
|
|
}
|
|
|
|
|
2012-09-20 14:24:47 +00:00
|
|
|
#ifdef WITH_UDEV
|
2014-03-25 06:52:40 +00:00
|
|
|
cleanup:
|
2010-11-12 15:49:40 +00:00
|
|
|
virCommandFree(cmd);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return serial;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSINewLun(virStoragePoolObjPtr pool,
|
2010-11-12 15:49:40 +00:00
|
|
|
uint32_t host ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
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) {
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
/* 'host' is dynamically allocated by the kernel, first come,
|
|
|
|
* first served, per HBA. As such it isn't suitable for use
|
|
|
|
* in the volume name. We only need uniqueness per-pool, so
|
|
|
|
* just leave 'host' out
|
|
|
|
*/
|
|
|
|
if (virAsprintf(&(vol->name), "unit:%u:%u:%u", bus, target, lun) < 0) {
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
|
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Trying to create volume for '%s'", devpath);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
/* 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...
|
|
|
|
*/
|
2010-02-04 20:02:58 +00:00
|
|
|
if ((vol->target.path = virStorageBackendStablePath(pool,
|
2012-10-21 16:53:20 +00:00
|
|
|
devpath,
|
|
|
|
true)) == NULL) {
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2012-05-25 13:14:07 +00:00
|
|
|
if (STREQ(devpath, vol->target.path) &&
|
2009-04-01 16:03:22 +00:00
|
|
|
!(STREQ(pool->def->target.path, "/dev") ||
|
|
|
|
STREQ(pool->def->target.path, "/dev/"))) {
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("No stable path found for '%s' in '%s'",
|
2009-04-01 16:03:22 +00:00
|
|
|
devpath, pool->def->target.path);
|
|
|
|
|
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2014-04-01 23:52:41 +00:00
|
|
|
if (virStorageBackendUpdateVolInfo(vol, true,
|
2014-03-30 22:50:36 +00:00
|
|
|
VIR_STORAGE_VOL_OPEN_DEFAULT) < 0) {
|
2012-07-18 11:38:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to update volume for '%s'"),
|
|
|
|
devpath);
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
if (!(vol->key = virStorageBackendSCSISerial(vol->target.path))) {
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
conf: track sizes directly in source struct
One of the features of qcow2 is that a wrapper file can have
more capacity than its backing file from the guest's perspective;
what's more, sparse files make tracking allocation of both
the active and backing file worthwhile. As such, it makes
more sense to show allocation numbers for each file in a chain,
and not just the top-level file. This sets up the fields for
the tracking, although it does not modify XML to display any
new information.
* src/util/virstoragefile.h (_virStorageSource): Add fields.
* src/conf/storage_conf.h (_virStorageVolDef): Drop redundant
fields.
* src/storage/storage_backend.c (virStorageBackendCreateBlockFrom)
(createRawFile, virStorageBackendCreateQemuImgCmd)
(virStorageBackendCreateQcowCreate): Update clients.
* src/storage/storage_driver.c (storageVolDelete)
(storageVolCreateXML, storageVolCreateXMLFrom, storageVolResize)
(storageVolWipeInternal, storageVolGetInfo): Likewise.
* src/storage/storage_backend_fs.c (virStorageBackendProbeTarget)
(virStorageBackendFileSystemRefresh)
(virStorageBackendFileSystemVolResize)
(virStorageBackendFileSystemVolRefresh): Likewise.
* src/storage/storage_backend_logical.c
(virStorageBackendLogicalMakeVol)
(virStorageBackendLogicalCreateVol): Likewise.
* src/storage/storage_backend_scsi.c
(virStorageBackendSCSINewLun): Likewise.
* src/storage/storage_backend_mpath.c
(virStorageBackendMpathNewVol): Likewise.
* src/storage/storage_backend_rbd.c
(volStorageBackendRBDRefreshVolInfo)
(virStorageBackendRBDCreateImage): Likewise.
* src/storage/storage_backend_disk.c
(virStorageBackendDiskMakeDataVol)
(virStorageBackendDiskCreateVol): Likewise.
* src/storage/storage_backend_sheepdog.c
(virStorageBackendSheepdogBuildVol)
(virStorageBackendSheepdogParseVdiList): Likewise.
* src/storage/storage_backend_gluster.c
(virStorageBackendGlusterRefreshVol): Likewise.
* src/conf/storage_conf.c (virStorageVolDefFormat)
(virStorageVolDefParseXML): Likewise.
* src/test/test_driver.c (testOpenVolumesForPool)
(testStorageVolCreateXML, testStorageVolCreateXMLFrom)
(testStorageVolDelete, testStorageVolGetInfo): Likewise.
* src/esx/esx_storage_backend_iscsi.c (esxStorageVolGetXMLDesc):
Likewise.
* src/esx/esx_storage_backend_vmfs.c (esxStorageVolGetXMLDesc)
(esxStorageVolCreateXML): Likewise.
* src/parallels/parallels_driver.c (parallelsAddHddByVolume):
Likewise.
* src/parallels/parallels_storage.c (parallelsDiskDescParseNode)
(parallelsStorageVolDefineXML, parallelsStorageVolCreateXMLFrom)
(parallelsStorageVolDefRemove, parallelsStorageVolGetInfo):
Likewise.
* src/vbox/vbox_tmpl.c (vboxStorageVolCreateXML)
(vboxStorageVolGetXMLDesc): Likewise.
* tests/storagebackendsheepdogtest.c (test_vdi_list_parser):
Likewise.
* src/phyp/phyp_driver.c (phypStorageVolCreateXML): Likewise.
2014-04-01 23:43:36 +00:00
|
|
|
pool->def->capacity += vol->target.capacity;
|
|
|
|
pool->def->allocation += vol->target.allocation;
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2014-03-07 08:33:31 +00:00
|
|
|
if (VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0) {
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
free_vol:
|
2009-04-01 16:03:22 +00:00
|
|
|
virStorageVolDefFree(vol);
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
VIR_FREE(devpath);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getNewStyleBlockDevice(const char *lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
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;
|
|
|
|
|
2013-07-04 10:16:29 +00:00
|
|
|
if (virAsprintf(&block_path, "%s/block", lun_path) < 0)
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Looking for block device in '%s'", block_path);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
block_dir = opendir(block_path);
|
|
|
|
if (block_dir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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;
|
|
|
|
}
|
|
|
|
|
2013-05-03 12:49:08 +00:00
|
|
|
if (VIR_STRDUP(*block_device, block_dirent->d_name) < 0) {
|
2009-11-08 21:08:54 +00:00
|
|
|
closedir(block_dir);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Block device is '%s'", *block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(block_dir);
|
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
VIR_FREE(block_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getOldStyleBlockDevice(const char *lun_path ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
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 */
|
2012-07-18 11:38:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to parse block name %s"),
|
|
|
|
block_name);
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
} else {
|
|
|
|
blockp++;
|
2013-05-03 12:49:08 +00:00
|
|
|
if (VIR_STRDUP(*block_device, blockp) < 0) {
|
2009-11-08 21:08:54 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Block device is '%s'", *block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getBlockDevice(uint32_t host,
|
2009-04-01 16:03:22 +00:00
|
|
|
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",
|
2013-07-04 10:16:29 +00:00
|
|
|
host, bus, target, lun) < 0)
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
lun_dir = opendir(lun_path);
|
|
|
|
if (lun_dir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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) {
|
2010-02-10 11:42:56 +00:00
|
|
|
retval = getNewStyleBlockDevice(lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
lun_dirent->d_name,
|
|
|
|
block_device);
|
|
|
|
} else {
|
2010-02-10 11:42:56 +00:00
|
|
|
retval = getOldStyleBlockDevice(lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
lun_dirent->d_name,
|
|
|
|
block_device);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(lun_dir);
|
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
VIR_FREE(lun_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
processLU(virStoragePoolObjPtr pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
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;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Processing LU %u:%u:%u:%u",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (getDeviceType(host, bus, target, lun, &device_type) < 0) {
|
2012-07-18 11:38:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
|
|
|
|
host, bus, target, lun);
|
2009-04-01 16:03:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("%u:%u:%u:%u is a Direct-Access LUN",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (getBlockDevice(host, bus, target, lun, &block_device) < 0) {
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (virStorageBackendSCSINewLun(pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun,
|
|
|
|
block_device) < 0) {
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Failed to create new storage volume for %u:%u:%u:%u",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Created new storage volume for %u:%u:%u:%u successfully",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
|
|
|
VIR_FREE(type_path);
|
|
|
|
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2010-12-10 12:26:41 +00:00
|
|
|
VIR_FREE(block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
uint32_t scanhost)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
uint32_t bus, target, lun;
|
2013-06-07 13:20:35 +00:00
|
|
|
const char *device_path = "/sys/bus/scsi/devices";
|
2009-04-01 16:03:22 +00:00
|
|
|
DIR *devicedir = NULL;
|
|
|
|
struct dirent *lun_dirent = NULL;
|
|
|
|
char devicepattern[64];
|
storage: Add document for possible problem on volume detection
For pool which relies on remote resources, such as a "iscsi" type
pool, since how long it takes to export the corresponding devices
to host's sysfs is really depended, it could depend on the network
connection, it also could depend on the host's udev procedures. So
it's likely that the volumes are not able to be detected during pool
starting process, polling the sysfs doesn't work, since we don't
know how much time is best for the polling, and even worse, the
volumes could still be not detected or partly not detected even after
the polling. So we end up with a documentation to prompt the fact,
in virsh manual.
And as a small improvement, let's explicitly say no LUNs found in
the debug log in that case.
2014-01-22 14:39:42 +00:00
|
|
|
bool found = false;
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Discovering LUs on host %u", scanhost);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
virFileWaitForDevices();
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
devicedir = opendir(device_path);
|
|
|
|
|
|
|
|
if (devicedir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to opendir path '%s'"), device_path);
|
2013-06-07 13:20:35 +00:00
|
|
|
return -1;
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
storage: Add document for possible problem on volume detection
For pool which relies on remote resources, such as a "iscsi" type
pool, since how long it takes to export the corresponding devices
to host's sysfs is really depended, it could depend on the network
connection, it also could depend on the host's udev procedures. So
it's likely that the volumes are not able to be detected during pool
starting process, polling the sysfs doesn't work, since we don't
know how much time is best for the polling, and even worse, the
volumes could still be not detected or partly not detected even after
the polling. So we end up with a documentation to prompt the fact,
in virsh manual.
And as a small improvement, let's explicitly say no LUNs found in
the debug log in that case.
2014-01-22 14:39:42 +00:00
|
|
|
found = true;
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Found LU '%s'", lun_dirent->d_name);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
processLU(pool, scanhost, bus, target, lun);
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
storage: Add document for possible problem on volume detection
For pool which relies on remote resources, such as a "iscsi" type
pool, since how long it takes to export the corresponding devices
to host's sysfs is really depended, it could depend on the network
connection, it also could depend on the host's udev procedures. So
it's likely that the volumes are not able to be detected during pool
starting process, polling the sysfs doesn't work, since we don't
know how much time is best for the polling, and even worse, the
volumes could still be not detected or partly not detected even after
the polling. So we end up with a documentation to prompt the fact,
in virsh manual.
And as a small improvement, let's explicitly say no LUNs found in
the debug log in that case.
2014-01-22 14:39:42 +00:00
|
|
|
if (!found)
|
|
|
|
VIR_DEBUG("No LU found for pool %s", pool->def->name);
|
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
closedir(devicedir);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-04-07 12:50:17 +00:00
|
|
|
static int
|
2010-02-04 20:02:58 +00:00
|
|
|
virStorageBackendSCSITriggerRescan(uint32_t host)
|
2009-04-07 12:50:17 +00:00
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int retval = 0;
|
|
|
|
char *path;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Triggering rescan of host %d", host);
|
2009-04-07 12:50:17 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&path, "/sys/class/scsi_host/host%u/scan", host) < 0) {
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Scan trigger path is '%s'", path);
|
2009-04-07 12:50:17 +00:00
|
|
|
|
|
|
|
fd = open(path, O_WRONLY);
|
|
|
|
|
|
|
|
if (fd < 0) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-07 12:50:17 +00:00
|
|
|
_("Could not open '%s' to trigger host scan"),
|
|
|
|
path);
|
|
|
|
retval = -1;
|
|
|
|
goto free_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd,
|
|
|
|
LINUX_SYSFS_SCSI_HOST_SCAN_STRING,
|
|
|
|
sizeof(LINUX_SYSFS_SCSI_HOST_SCAN_STRING)) < 0) {
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-07 12:50:17 +00:00
|
|
|
_("Write to '%s' to trigger host scan failed"),
|
|
|
|
path);
|
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2014-03-25 06:52:40 +00:00
|
|
|
free_path:
|
2009-04-07 12:50:17 +00:00
|
|
|
VIR_FREE(path);
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Rescan of host %d complete", host);
|
2009-04-07 12:50:17 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:37 +00:00
|
|
|
static int
|
|
|
|
getHostNumber(const char *adapter_name,
|
|
|
|
unsigned int *result)
|
|
|
|
{
|
|
|
|
char *host = (char *)adapter_name;
|
|
|
|
|
|
|
|
/* Specifying adapter like 'host5' is still supported for
|
|
|
|
* back-compat reason.
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(host, "scsi_host")) {
|
|
|
|
host += strlen("scsi_host");
|
2013-03-25 16:43:40 +00:00
|
|
|
} else if (STRPREFIX(host, "fc_host")) {
|
|
|
|
host += strlen("fc_host");
|
2013-03-25 16:43:37 +00:00
|
|
|
} else if (STRPREFIX(host, "host")) {
|
|
|
|
host += strlen("host");
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid adapter name '%s' for SCSI pool"),
|
|
|
|
adapter_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result && virStrToLong_ui(host, NULL, 10, result) == -1) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid adapter name '%s' for SCSI pool"),
|
|
|
|
adapter_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:40 +00:00
|
|
|
static char *
|
|
|
|
getAdapterName(virStoragePoolSourceAdapter adapter)
|
|
|
|
{
|
|
|
|
char *name = NULL;
|
|
|
|
|
2013-05-03 12:49:08 +00:00
|
|
|
if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) {
|
|
|
|
ignore_value(VIR_STRDUP(name, adapter.data.name));
|
|
|
|
return name;
|
|
|
|
}
|
2013-03-25 16:43:40 +00:00
|
|
|
|
|
|
|
if (!(name = virGetFCHostNameByWWN(NULL,
|
|
|
|
adapter.data.fchost.wwnn,
|
|
|
|
adapter.data.fchost.wwpn))) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("Failed to find SCSI host with wwnn='%s', "
|
|
|
|
"wwpn='%s'"), adapter.data.fchost.wwnn,
|
|
|
|
adapter.data.fchost.wwpn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:41 +00:00
|
|
|
static int
|
|
|
|
createVport(virStoragePoolSourceAdapter adapter)
|
|
|
|
{
|
|
|
|
unsigned int parent_host;
|
|
|
|
char *name = NULL;
|
|
|
|
|
|
|
|
if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* This filters either HBA or already created vHBA */
|
|
|
|
if ((name = virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn,
|
|
|
|
adapter.data.fchost.wwpn))) {
|
|
|
|
VIR_FREE(name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:42 +00:00
|
|
|
if (!adapter.data.fchost.parent &&
|
|
|
|
!(adapter.data.fchost.parent = virFindFCHostCapableVport(NULL))) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
|
|
_("'parent' for vHBA not specified, and "
|
|
|
|
"cannot find one on this host"));
|
|
|
|
return -1;
|
2013-03-25 16:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-06-28 09:59:51 +00:00
|
|
|
if (virManageVport(parent_host, adapter.data.fchost.wwpn,
|
|
|
|
adapter.data.fchost.wwnn, VPORT_CREATE) < 0)
|
2013-03-25 16:43:41 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
virFileWaitForDevices();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
deleteVport(virStoragePoolSourceAdapter adapter)
|
|
|
|
{
|
|
|
|
unsigned int parent_host;
|
2014-01-23 10:18:36 +00:00
|
|
|
char *name = NULL;
|
|
|
|
int ret = -1;
|
2013-03-25 16:43:41 +00:00
|
|
|
|
|
|
|
if (adapter.type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
|
|
|
|
return 0;
|
|
|
|
|
2013-11-20 12:38:17 +00:00
|
|
|
/* It must be a HBA instead of a vHBA as long as "parent"
|
|
|
|
* is NULL. "createVport" guaranteed "parent" for a vHBA
|
|
|
|
* cannot be NULL, it's either specified in XML, or detected
|
|
|
|
* automatically.
|
|
|
|
*/
|
|
|
|
if (!adapter.data.fchost.parent)
|
|
|
|
return 0;
|
|
|
|
|
2014-01-23 10:18:36 +00:00
|
|
|
if (!(name = virGetFCHostNameByWWN(NULL, adapter.data.fchost.wwnn,
|
|
|
|
adapter.data.fchost.wwpn)))
|
2013-03-25 16:43:41 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (getHostNumber(adapter.data.fchost.parent, &parent_host) < 0)
|
2014-01-23 10:18:36 +00:00
|
|
|
goto cleanup;
|
2013-03-25 16:43:41 +00:00
|
|
|
|
2013-06-28 09:59:51 +00:00
|
|
|
if (virManageVport(parent_host, adapter.data.fchost.wwpn,
|
|
|
|
adapter.data.fchost.wwnn, VPORT_DELETE) < 0)
|
2014-01-23 10:18:36 +00:00
|
|
|
goto cleanup;
|
2013-03-25 16:43:41 +00:00
|
|
|
|
2014-01-23 10:18:36 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:52:40 +00:00
|
|
|
cleanup:
|
2014-01-23 10:18:36 +00:00
|
|
|
VIR_FREE(name);
|
|
|
|
return ret;
|
2013-03-25 16:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
static int
|
|
|
|
virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
bool *isActive)
|
|
|
|
{
|
2013-03-25 16:43:40 +00:00
|
|
|
char *path = NULL;
|
|
|
|
char *name = NULL;
|
2013-03-25 16:43:37 +00:00
|
|
|
unsigned int host;
|
2013-03-25 16:43:40 +00:00
|
|
|
int ret = -1;
|
2010-11-11 20:09:20 +00:00
|
|
|
|
|
|
|
*isActive = false;
|
2013-03-25 16:43:37 +00:00
|
|
|
|
2014-01-06 10:19:34 +00:00
|
|
|
if (!(name = getAdapterName(pool->def->source.adapter))) {
|
|
|
|
/* It's normal for the pool with "fc_host" type source
|
|
|
|
* adapter fails to get the adapter name, since the vHBA
|
|
|
|
* the adapter based on might be not created yet.
|
|
|
|
*/
|
|
|
|
if (pool->def->source.adapter.type ==
|
|
|
|
VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST) {
|
|
|
|
virResetLastError();
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2013-03-25 16:43:37 +00:00
|
|
|
|
2013-03-25 16:43:40 +00:00
|
|
|
if (getHostNumber(name, &host) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-04 10:16:29 +00:00
|
|
|
if (virAsprintf(&path, "/sys/class/scsi_host/host%d", host) < 0)
|
2013-03-25 16:43:40 +00:00
|
|
|
goto cleanup;
|
2010-11-11 20:09:20 +00:00
|
|
|
|
2013-09-13 13:32:43 +00:00
|
|
|
*isActive = virFileExists(path);
|
2010-11-11 20:09:20 +00:00
|
|
|
|
2013-03-25 16:43:40 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:52:40 +00:00
|
|
|
cleanup:
|
2010-11-11 20:09:20 +00:00
|
|
|
VIR_FREE(path);
|
2013-03-25 16:43:40 +00:00
|
|
|
VIR_FREE(name);
|
|
|
|
return ret;
|
2010-11-11 20:09:20 +00:00
|
|
|
}
|
2009-04-07 12:50:17 +00:00
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
2013-03-25 16:43:40 +00:00
|
|
|
char *name = NULL;
|
2013-03-25 16:43:37 +00:00
|
|
|
unsigned int host;
|
2013-03-25 16:43:40 +00:00
|
|
|
int ret = -1;
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
|
|
|
|
|
2013-03-25 16:43:40 +00:00
|
|
|
if (!(name = getAdapterName(pool->def->source.adapter)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (getHostNumber(name, &host) < 0)
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Scanning host%u", host);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2013-03-25 16:43:37 +00:00
|
|
|
if (virStorageBackendSCSITriggerRescan(host) < 0)
|
2009-04-07 12:50:17 +00:00
|
|
|
goto out;
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIFindLUs(pool, host);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2013-03-25 16:43:37 +00:00
|
|
|
ret = 0;
|
2014-03-25 06:52:40 +00:00
|
|
|
out:
|
2013-03-25 16:43:40 +00:00
|
|
|
VIR_FREE(name);
|
2013-03-25 16:43:37 +00:00
|
|
|
return ret;
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
2013-03-25 16:43:41 +00:00
|
|
|
static int
|
|
|
|
virStorageBackendSCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
virStoragePoolSourceAdapter adapter = pool->def->source.adapter;
|
|
|
|
return createVport(adapter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendSCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
virStoragePoolSourceAdapter adapter = pool->def->source.adapter;
|
|
|
|
return deleteVport(adapter);
|
|
|
|
}
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
virStorageBackend virStorageBackendSCSI = {
|
|
|
|
.type = VIR_STORAGE_POOL_SCSI,
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
.checkPool = virStorageBackendSCSICheckPool,
|
2009-04-01 16:03:22 +00:00
|
|
|
.refreshPool = virStorageBackendSCSIRefreshPool,
|
2013-03-25 16:43:41 +00:00
|
|
|
.startPool = virStorageBackendSCSIStartPool,
|
|
|
|
.stopPool = virStorageBackendSCSIStopPool,
|
2009-04-01 16:03:22 +00:00
|
|
|
};
|