2008-02-20 15:45:33 +00:00
|
|
|
/*
|
|
|
|
* storage_backend_logvol.c: storage backend for logical volume handling
|
|
|
|
*
|
2009-01-29 12:10:32 +00:00
|
|
|
* Copyright (C) 2007-2009 Red Hat, Inc.
|
2008-02-20 15:45:33 +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
|
|
|
|
* 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 <sys/wait.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <regex.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2008-11-04 22:30:33 +00:00
|
|
|
#include "virterror_internal.h"
|
2008-02-20 15:45:33 +00:00
|
|
|
#include "storage_backend_logical.h"
|
|
|
|
#include "storage_conf.h"
|
|
|
|
#include "util.h"
|
2008-06-06 11:09:57 +00:00
|
|
|
#include "memory.h"
|
2008-02-20 15:45:33 +00:00
|
|
|
|
2009-01-20 17:13:33 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
|
2008-02-20 15:45:33 +00:00
|
|
|
#define PV_BLANK_SECTOR_SIZE 512
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalSetActive(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
int on)
|
|
|
|
{
|
|
|
|
const char *cmdargv[4];
|
|
|
|
|
|
|
|
cmdargv[0] = VGCHANGE;
|
|
|
|
cmdargv[1] = on ? "-ay" : "-an";
|
2008-09-02 14:15:42 +00:00
|
|
|
cmdargv[2] = pool->def->source.name;
|
2008-02-20 15:45:33 +00:00
|
|
|
cmdargv[3] = NULL;
|
|
|
|
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, cmdargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalMakeVol(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
char **const groups,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
virStorageVolDefPtr vol = NULL;
|
|
|
|
unsigned long long offset, size, length;
|
|
|
|
|
|
|
|
/* See if we're only looking for a specific volume */
|
|
|
|
if (data != NULL) {
|
|
|
|
vol = data;
|
|
|
|
if (STRNEQ(vol->name, groups[0]))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Or filling in more data on an existing volume */
|
|
|
|
if (vol == NULL)
|
|
|
|
vol = virStorageVolDefFindByName(pool, groups[0]);
|
|
|
|
|
|
|
|
/* Or a completely new volume */
|
|
|
|
if (vol == NULL) {
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_ALLOC(vol) < 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-11-17 11:19:33 +00:00
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
2008-02-20 15:45:33 +00:00
|
|
|
if ((vol->name = strdup(groups[0])) == NULL) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-10-10 15:13:28 +00:00
|
|
|
virStorageVolDefFree(vol);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-10-10 15:13:28 +00:00
|
|
|
if (VIR_REALLOC_N(pool->volumes.objs,
|
|
|
|
pool->volumes.count + 1)) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-10-10 15:13:28 +00:00
|
|
|
virStorageVolDefFree(vol);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pool->volumes.objs[pool->volumes.count++] = vol;
|
2008-02-20 15:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vol->target.path == NULL) {
|
2009-01-27 18:30:03 +00:00
|
|
|
if (virAsprintf(&vol->target.path, "%s/%s",
|
|
|
|
pool->def->target.path, vol->name) < 0) {
|
|
|
|
virReportOOMError(conn);
|
|
|
|
virStorageVolDefFree(vol);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (groups[1] && !STREQ(groups[1], "")) {
|
|
|
|
if (virAsprintf(&vol->backingStore.path, "%s/%s",
|
|
|
|
pool->def->target.path, groups[1]) < 0) {
|
|
|
|
virReportOOMError(conn);
|
|
|
|
virStorageVolDefFree(vol);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-27 18:30:03 +00:00
|
|
|
|
|
|
|
vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2;
|
2008-02-20 15:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (vol->key == NULL &&
|
2009-01-27 18:30:03 +00:00
|
|
|
(vol->key = strdup(groups[2])) == NULL) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
/* Finally fill in extents information */
|
2008-06-06 11:09:57 +00:00
|
|
|
if (VIR_REALLOC_N(vol->source.extents,
|
|
|
|
vol->source.nextent + 1) < 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((vol->source.extents[vol->source.nextent].path =
|
2009-01-27 18:30:03 +00:00
|
|
|
strdup(groups[3])) == NULL) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-01-27 18:30:03 +00:00
|
|
|
if (virStrToLong_ull(groups[4], NULL, 10, &offset) < 0) {
|
2008-02-20 15:45:33 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed volume extent offset value"));
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-27 18:30:03 +00:00
|
|
|
if (virStrToLong_ull(groups[5], NULL, 10, &length) < 0) {
|
2008-02-20 15:45:33 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed volume extent length value"));
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2009-01-27 18:30:03 +00:00
|
|
|
if (virStrToLong_ull(groups[6], NULL, 10, &size) < 0) {
|
2008-02-20 15:45:33 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
2008-02-22 16:26:13 +00:00
|
|
|
"%s", _("malformed volume extent size value"));
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vol->source.extents[vol->source.nextent].start = offset * size;
|
|
|
|
vol->source.extents[vol->source.nextent].end = (offset * size) + length;
|
|
|
|
vol->source.nextent++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalFindLVs(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr vol)
|
|
|
|
{
|
|
|
|
/*
|
2009-01-27 18:30:03 +00:00
|
|
|
* # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,origin,uuid,devices,seg_size,vg_extent_size" VGNAME
|
|
|
|
* RootLV,,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432
|
|
|
|
* SwapLV,,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432
|
|
|
|
* Test2,,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432
|
|
|
|
* Test3,,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432
|
|
|
|
* Test3,Test2,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432
|
2008-02-20 15:45:33 +00:00
|
|
|
*
|
2009-01-27 18:30:03 +00:00
|
|
|
* Pull out name, origin, & uuid, device, device extent start #, segment size, extent size.
|
2008-02-20 15:45:33 +00:00
|
|
|
*
|
|
|
|
* NB can be multiple rows per volume if they have many extents
|
2008-07-30 08:52:44 +00:00
|
|
|
*
|
2008-11-14 16:10:47 +00:00
|
|
|
* NB lvs from some distros (e.g. SLES10 SP2) outputs trailing "," on each line
|
|
|
|
*
|
|
|
|
* NB Encrypted logical volumes can print ':' in their name, so it is
|
|
|
|
* not a suitable separator (rhbz 470693).
|
2008-02-20 15:45:33 +00:00
|
|
|
*/
|
|
|
|
const char *regexes[] = {
|
2009-01-27 18:30:03 +00:00
|
|
|
"^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$"
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
|
|
|
int vars[] = {
|
2009-01-27 18:30:03 +00:00
|
|
|
7
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
|
|
|
const char *prog[] = {
|
2008-11-14 16:10:47 +00:00
|
|
|
LVS, "--separator", ",", "--noheadings", "--units", "b",
|
2008-02-20 15:45:33 +00:00
|
|
|
"--unbuffered", "--nosuffix", "--options",
|
2009-01-27 18:30:03 +00:00
|
|
|
"lv_name,origin,uuid,devices,seg_size,vg_extent_size",
|
2008-09-02 14:15:42 +00:00
|
|
|
pool->def->source.name, NULL
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
|
|
|
|
2008-06-17 12:45:24 +00:00
|
|
|
int exitstatus;
|
|
|
|
|
|
|
|
if (virStorageBackendRunProgRegex(conn,
|
|
|
|
pool,
|
|
|
|
prog,
|
|
|
|
1,
|
|
|
|
regexes,
|
|
|
|
vars,
|
|
|
|
virStorageBackendLogicalMakeVol,
|
|
|
|
vol,
|
|
|
|
&exitstatus) < 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
avoid many format string warnings
Building with --disable-nls exposed many new warnings like these:
virsh.c:4952: warning: format not a string literal and no format ...
util.c:163: warning: format not a string literal and no format arguments
All but one of the following changes add a "%s" argument before
the offending _(...) argument.
This was the only manual change:
* src/lxc_driver.c (lxcVersion): Use %s and strerror(errno)
rather than %m, to avoid a warning from gcc -Wformat-security.
Add "%s" before each warned about format-string-with-no-%-directive:
* src/domain_conf.c (virDomainHostdevSubsysUsbDefParseXML)
(virDomainDefParseString, virDomainDefParseFile):
* src/hash.c (virGetConnect, __virGetDomain, virReleaseDomain)
(__virGetNetwork, virReleaseNetwork, __virGetStoragePool)
(virReleaseStoragePool, __virGetStorageVol, virReleaseStorageVol):
* src/lxc_container.c (lxcContainerChild):
* src/lxc_driver.c (lxcDomainDefine, lxcDomainUndefine)
(lxcDomainGetInfo, lxcGetOSType, lxcDomainDumpXML)
(lxcSetupInterfaces, lxcDomainStart, lxcDomainCreateAndStart)
(lxcVersion, lxcGetSchedulerParameters):
* src/network_conf.c (virNetworkDefParseString)
(virNetworkDefParseFile):
* src/openvz_conf.c (openvzReadNetworkConf, openvzLoadDomains):
* src/openvz_driver.c (openvzDomainDefineCmd)
(openvzDomainGetInfo, openvzDomainDumpXML, openvzDomainShutdown)
(openvzDomainReboot, ADD_ARG_LIT, openvzDomainDefineXML)
(openvzDomainCreateXML, openvzDomainCreate, openvzDomainUndefine)
(openvzDomainSetAutostart, openvzDomainGetAutostart)
(openvzDomainSetVcpus):
* src/qemu_driver.c (qemudDomainBlockPeek, qemudDomainMemoryPeek):
* src/remote_internal.c (remoteDomainBlockPeek)
(remoteDomainMemoryPeek, remoteAuthPolkit):
* src/sexpr.c (sexpr_new, _string2sexpr):
* src/storage_backend_disk.c (virStorageBackendDiskMakeDataVol)
(virStorageBackendDiskCreateVol):
* src/storage_backend_fs.c
(virStorageBackendFileSystemNetFindPoolSources):
* src/storage_backend_logical.c (virStorageBackendLogicalFindLVs)
(virStorageBackendLogicalFindPoolSources):
* src/test.c (testOpenDefault, testOpenFromFile, testOpen)
(testGetDomainInfo, testDomainRestore)
(testNodeGetCellsFreeMemory):
* src/util.c (virExec):
* src/virsh.c (cmdAttachDevice, cmdDetachDevice)
(cmdAttachInterface, cmdDetachInterface, cmdAttachDisk)
(cmdDetachDisk, cmdEdit):
* src/xend_internal.c (do_connect, wr_sync, xend_op_ext)
(urlencode, xenDaemonDomainCreateXML)
(xenDaemonDomainLookupByName_ids, xenDaemonDomainLookupByID)
(xenDaemonParseSxprOS, xend_parse_sexp_desc_char)
(xenDaemonParseSxprChar, xenDaemonParseSxprDisks)
(xenDaemonParseSxpr, sexpr_to_xend_topology, sexpr_to_domain)
(xenDaemonDomainFetch, xenDaemonDomainGetAutostart)
(xenDaemonDomainSetAutostart, xenDaemonDomainMigratePerform)
(xenDaemonDomainDefineXML, xenDaemonGetSchedulerType)
(xenDaemonGetSchedulerParameters)
(xenDaemonSetSchedulerParameters, xenDaemonDomainBlockPeek)
(xenDaemonFormatSxprChr, virDomainXMLDevID):
* src/xm_internal.c (xenXMConfigCacheRefresh, xenXMDomainPinVcpu)
(xenXMDomainCreate, xenXMDomainDefineXML)
(xenXMDomainAttachDevice, xenXMDomainDetachDevice):
* src/xml.c (virXPathString, virXPathNumber, virXPathLong)
(virXPathULong, virXPathBoolean, virXPathNode, virXPathNodeSet):
* src/xs_internal.c (xenStoreOpen):
2008-10-13 16:46:28 +00:00
|
|
|
"%s", _("lvs command failed"));
|
2008-06-17 12:45:24 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exitstatus != 0) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("lvs command failed with exitstatus %d"),
|
|
|
|
exitstatus);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2008-02-20 15:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalRefreshPoolFunc(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
|
|
char **const groups,
|
|
|
|
void *data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
if (virStrToLong_ull(groups[0], NULL, 10, &pool->def->capacity) < 0)
|
|
|
|
return -1;
|
|
|
|
if (virStrToLong_ull(groups[1], NULL, 10, &pool->def->available) < 0)
|
|
|
|
return -1;
|
|
|
|
pool->def->allocation = pool->def->capacity - pool->def->available;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-27 20:05:58 +00:00
|
|
|
static int
|
2008-10-23 11:39:53 +00:00
|
|
|
virStorageBackendLogicalFindPoolSourcesFunc(virConnectPtr conn,
|
2008-08-27 20:05:58 +00:00
|
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
|
|
char **const groups,
|
|
|
|
void *data)
|
|
|
|
{
|
2008-10-23 11:39:53 +00:00
|
|
|
virStoragePoolSourceListPtr sourceList = data;
|
|
|
|
char *pvname = NULL;
|
|
|
|
char *vgname = NULL;
|
|
|
|
int i;
|
|
|
|
virStoragePoolSourceDevicePtr dev;
|
|
|
|
virStoragePoolSource *thisSource;
|
|
|
|
|
|
|
|
pvname = strdup(groups[0]);
|
|
|
|
vgname = strdup(groups[1]);
|
|
|
|
|
|
|
|
if (pvname == NULL || vgname == NULL) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-10-23 11:39:53 +00:00
|
|
|
goto err_no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
thisSource = NULL;
|
|
|
|
for (i = 0 ; i < sourceList->nsources; i++) {
|
|
|
|
if (STREQ(sourceList->sources[i].name, vgname)) {
|
|
|
|
thisSource = &sourceList->sources[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-08-27 20:05:58 +00:00
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
if (thisSource == NULL) {
|
|
|
|
if (VIR_REALLOC_N(sourceList->sources, sourceList->nsources + 1) != 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-10-23 11:39:53 +00:00
|
|
|
goto err_no_memory;
|
|
|
|
}
|
2008-08-27 20:05:58 +00:00
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
thisSource = &sourceList->sources[sourceList->nsources];
|
|
|
|
sourceList->nsources++;
|
|
|
|
|
|
|
|
memset(thisSource, 0, sizeof(*thisSource));
|
|
|
|
thisSource->name = vgname;
|
2008-08-27 20:05:58 +00:00
|
|
|
}
|
2008-10-23 11:39:53 +00:00
|
|
|
else
|
|
|
|
VIR_FREE(vgname);
|
2008-08-27 20:05:58 +00:00
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
if (VIR_REALLOC_N(thisSource->devices, thisSource->ndevice + 1) != 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-10-23 11:39:53 +00:00
|
|
|
goto err_no_memory;
|
2008-08-27 20:05:58 +00:00
|
|
|
}
|
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
dev = &thisSource->devices[thisSource->ndevice];
|
|
|
|
thisSource->ndevice++;
|
2008-11-04 21:54:21 +00:00
|
|
|
thisSource->format = VIR_STORAGE_POOL_LOGICAL_LVM2;
|
2008-10-23 11:39:53 +00:00
|
|
|
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
dev->path = pvname;
|
2008-08-27 20:05:58 +00:00
|
|
|
|
|
|
|
return 0;
|
2008-10-23 11:39:53 +00:00
|
|
|
|
|
|
|
err_no_memory:
|
|
|
|
VIR_FREE(pvname);
|
|
|
|
VIR_FREE(vgname);
|
|
|
|
|
|
|
|
return -1;
|
2008-08-27 20:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
virStorageBackendLogicalFindPoolSources(virConnectPtr conn,
|
|
|
|
const char *srcSpec ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
/*
|
2008-10-23 11:39:53 +00:00
|
|
|
* # pvs --noheadings -o pv_name,vg_name
|
|
|
|
* /dev/sdb
|
|
|
|
* /dev/sdc VolGroup00
|
2008-08-27 20:05:58 +00:00
|
|
|
*/
|
|
|
|
const char *regexes[] = {
|
2008-10-23 11:39:53 +00:00
|
|
|
"^\\s*(\\S+)\\s+(\\S+)\\s*$"
|
2008-08-27 20:05:58 +00:00
|
|
|
};
|
|
|
|
int vars[] = {
|
2008-10-23 11:39:53 +00:00
|
|
|
2
|
2008-08-27 20:05:58 +00:00
|
|
|
};
|
2008-10-23 11:39:53 +00:00
|
|
|
const char *const prog[] = { PVS, "--noheadings", "-o", "pv_name,vg_name", NULL };
|
2008-11-05 11:41:43 +00:00
|
|
|
const char *const scanprog[] = { VGSCAN, NULL };
|
2008-08-27 20:05:58 +00:00
|
|
|
int exitstatus;
|
|
|
|
char *retval = NULL;
|
2008-10-23 11:39:53 +00:00
|
|
|
virStoragePoolSourceList sourceList;
|
|
|
|
int i;
|
2008-08-27 20:05:58 +00:00
|
|
|
|
2008-11-05 11:41:43 +00:00
|
|
|
/*
|
|
|
|
* NOTE: ignoring errors here; this is just to "touch" any logical volumes
|
|
|
|
* that might be hanging around, so if this fails for some reason, the
|
|
|
|
* worst that happens is that scanning doesn't pick everything up
|
|
|
|
*/
|
|
|
|
virRun(conn, scanprog, &exitstatus);
|
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
memset(&sourceList, 0, sizeof(sourceList));
|
2008-11-04 21:54:21 +00:00
|
|
|
sourceList.type = VIR_STORAGE_POOL_LOGICAL;
|
|
|
|
|
2008-08-27 20:05:58 +00:00
|
|
|
if (virStorageBackendRunProgRegex(conn, NULL, prog, 1, regexes, vars,
|
|
|
|
virStorageBackendLogicalFindPoolSourcesFunc,
|
2008-10-23 11:39:53 +00:00
|
|
|
&sourceList, &exitstatus) < 0)
|
2008-08-27 20:05:58 +00:00
|
|
|
return NULL;
|
|
|
|
|
2008-10-23 11:39:53 +00:00
|
|
|
retval = virStoragePoolSourceListFormat(conn, &sourceList);
|
2008-08-27 20:05:58 +00:00
|
|
|
if (retval == NULL) {
|
2008-10-23 11:39:53 +00:00
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("failed to get source from sourceList"));
|
2008-08-27 20:05:58 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2008-10-23 11:39:53 +00:00
|
|
|
for (i = 0; i < sourceList.nsources; i++)
|
|
|
|
virStoragePoolSourceFree(&sourceList.sources[i]);
|
|
|
|
|
|
|
|
VIR_FREE(sourceList.sources);
|
2008-08-27 20:05:58 +00:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-20 15:45:33 +00:00
|
|
|
static int
|
|
|
|
virStorageBackendLogicalStartPool(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
if (virStorageBackendLogicalSetActive(conn, pool, 1) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalBuildPool(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
const char **vgargv;
|
|
|
|
const char *pvargv[3];
|
|
|
|
int n = 0, i, fd;
|
|
|
|
char zeros[PV_BLANK_SECTOR_SIZE];
|
|
|
|
|
|
|
|
memset(zeros, 0, sizeof(zeros));
|
|
|
|
|
2008-09-04 13:13:32 +00:00
|
|
|
if (VIR_ALLOC_N(vgargv, 3 + pool->def->source.ndevice) < 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vgargv[n++] = VGCREATE;
|
2008-09-02 14:15:42 +00:00
|
|
|
vgargv[n++] = pool->def->source.name;
|
2008-02-20 15:45:33 +00:00
|
|
|
|
|
|
|
pvargv[0] = PVCREATE;
|
|
|
|
pvargv[2] = NULL;
|
|
|
|
for (i = 0 ; i < pool->def->source.ndevice ; i++) {
|
|
|
|
/*
|
|
|
|
* LVM requires that the first sector is blanked if using
|
|
|
|
* a whole disk as a PV. So we just blank them out regardless
|
|
|
|
* rather than trying to figure out if we're a disk or partition
|
|
|
|
*/
|
|
|
|
if ((fd = open(pool->def->source.devices[i].path, O_WRONLY)) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot open device '%s'"),
|
|
|
|
pool->def->source.devices[i].path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2008-02-25 10:00:16 +00:00
|
|
|
if (safewrite(fd, zeros, sizeof(zeros)) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot clear device header of '%s'"),
|
|
|
|
pool->def->source.devices[i].path);
|
2008-02-20 15:45:33 +00:00
|
|
|
close(fd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (close(fd) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot close device '%s'"),
|
|
|
|
pool->def->source.devices[i].path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the physical volume because vgcreate is not
|
|
|
|
* clever enough todo this for us :-(
|
|
|
|
*/
|
|
|
|
vgargv[n++] = pool->def->source.devices[i].path;
|
|
|
|
pvargv[1] = pool->def->source.devices[i].path;
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, pvargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
vgargv[n++] = NULL;
|
|
|
|
|
|
|
|
/* Now create the volume group itself */
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, vgargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(vgargv);
|
2008-02-20 15:45:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
2008-06-06 11:09:57 +00:00
|
|
|
VIR_FREE(vgargv);
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalRefreshPool(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* # vgs --separator : --noheadings --units b --unbuffered --nosuffix --options "vg_size,vg_free" VGNAME
|
|
|
|
* 10603200512:4328521728
|
|
|
|
*
|
|
|
|
* Pull out size & free
|
2008-07-30 08:52:44 +00:00
|
|
|
*
|
|
|
|
* NB vgs from some distros (e.g. SLES10 SP2) outputs trailing ":" on each line
|
2008-02-20 15:45:33 +00:00
|
|
|
*/
|
|
|
|
const char *regexes[] = {
|
2008-07-30 08:52:44 +00:00
|
|
|
"^\\s*(\\S+):([0-9]+):?\\s*$"
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
|
|
|
int vars[] = {
|
|
|
|
2
|
|
|
|
};
|
|
|
|
const char *prog[] = {
|
|
|
|
VGS, "--separator", ":", "--noheadings", "--units", "b", "--unbuffered",
|
|
|
|
"--nosuffix", "--options", "vg_size,vg_free",
|
2008-09-02 14:15:42 +00:00
|
|
|
pool->def->source.name, NULL
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
2008-06-17 12:45:24 +00:00
|
|
|
int exitstatus;
|
2008-02-20 15:45:33 +00:00
|
|
|
|
2008-11-28 07:50:20 +00:00
|
|
|
virStorageBackendWaitForDevices(conn);
|
|
|
|
|
2008-02-20 15:45:33 +00:00
|
|
|
/* Get list of all logical volumes */
|
|
|
|
if (virStorageBackendLogicalFindLVs(conn, pool, NULL) < 0) {
|
|
|
|
virStoragePoolObjClearVols(pool);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now get basic volgrp metadata */
|
|
|
|
if (virStorageBackendRunProgRegex(conn,
|
|
|
|
pool,
|
|
|
|
prog,
|
|
|
|
1,
|
|
|
|
regexes,
|
|
|
|
vars,
|
|
|
|
virStorageBackendLogicalRefreshPoolFunc,
|
2008-06-17 12:45:24 +00:00
|
|
|
NULL,
|
|
|
|
&exitstatus) < 0) {
|
|
|
|
virStoragePoolObjClearVols(pool);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exitstatus != 0) {
|
2008-02-20 15:45:33 +00:00
|
|
|
virStoragePoolObjClearVols(pool);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-09-22 20:22:35 +00:00
|
|
|
/*
|
|
|
|
* This is actually relatively safe; if you happen to try to "stop" the
|
|
|
|
* pool that your / is on, for instance, you will get failure like:
|
|
|
|
* "Can't deactivate volume group "VolGroup00" with 3 open logical volume(s)"
|
2008-02-20 15:45:33 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalStopPool(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
if (virStorageBackendLogicalSetActive(conn, pool, 0) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalDeletePool(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
const char *cmdargv[] = {
|
2008-09-23 20:48:49 +00:00
|
|
|
VGREMOVE, "-f", pool->def->source.name, NULL
|
2008-02-20 15:45:33 +00:00
|
|
|
};
|
2008-09-22 20:22:35 +00:00
|
|
|
const char *pvargv[3];
|
|
|
|
int i, error;
|
2008-02-20 15:45:33 +00:00
|
|
|
|
2008-09-22 20:22:35 +00:00
|
|
|
/* first remove the volume group */
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, cmdargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
|
2008-09-22 20:22:35 +00:00
|
|
|
/* now remove the pv devices and clear them out */
|
|
|
|
error = 0;
|
|
|
|
pvargv[0] = PVREMOVE;
|
|
|
|
pvargv[2] = NULL;
|
|
|
|
for (i = 0 ; i < pool->def->source.ndevice ; i++) {
|
|
|
|
pvargv[1] = pool->def->source.devices[i].path;
|
|
|
|
if (virRun(conn, pvargv, NULL) < 0) {
|
|
|
|
error = -1;
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot remove PV device '%s'"),
|
|
|
|
pool->def->source.devices[i].path);
|
2008-09-22 20:22:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-02-20 15:45:33 +00:00
|
|
|
|
2008-09-22 20:22:35 +00:00
|
|
|
return error;
|
2008-02-20 15:45:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalDeleteVol(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr vol,
|
|
|
|
unsigned int flags);
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalCreateVol(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr vol)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
char size[100];
|
2009-01-27 18:30:03 +00:00
|
|
|
const char *cmdargvnew[] = {
|
2008-02-20 15:45:33 +00:00
|
|
|
LVCREATE, "--name", vol->name, "-L", size,
|
|
|
|
pool->def->target.path, NULL
|
|
|
|
};
|
2009-01-27 18:30:03 +00:00
|
|
|
const char *cmdargvsnap[] = {
|
|
|
|
LVCREATE, "--name", vol->name, "-L", size,
|
|
|
|
"-s", vol->backingStore.path, NULL
|
|
|
|
};
|
|
|
|
const char **cmdargv = cmdargvnew;
|
|
|
|
|
|
|
|
if (vol->backingStore.path) {
|
|
|
|
if (vol->backingStore.format !=
|
|
|
|
VIR_STORAGE_POOL_LOGICAL_LVM2) {
|
|
|
|
virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("LVM snapshots must be backed by another LVM volume"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cmdargv = cmdargvsnap;
|
|
|
|
}
|
2008-02-20 15:45:33 +00:00
|
|
|
|
|
|
|
snprintf(size, sizeof(size)-1, "%lluK", vol->capacity/1024);
|
|
|
|
size[sizeof(size)-1] = '\0';
|
|
|
|
|
2008-11-17 11:19:33 +00:00
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
2008-08-20 13:33:01 +00:00
|
|
|
if (vol->target.path != NULL) {
|
|
|
|
/* A target path passed to CreateVol has no meaning */
|
|
|
|
VIR_FREE(vol->target.path);
|
|
|
|
}
|
|
|
|
if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) +
|
|
|
|
1 + strlen(vol->name) + 1) < 0) {
|
2009-01-29 12:10:32 +00:00
|
|
|
virReportOOMError(conn);
|
2008-08-20 13:33:01 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(vol->target.path, pool->def->target.path);
|
|
|
|
strcat(vol->target.path, "/");
|
|
|
|
strcat(vol->target.path, vol->name);
|
|
|
|
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, cmdargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot read path '%s'"),
|
|
|
|
vol->target.path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We can only chown/grp if root */
|
|
|
|
if (getuid() == 0) {
|
|
|
|
if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot set file owner '%s'"),
|
|
|
|
vol->target.path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fchmod(fd, vol->target.perms.mode) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot set file mode '%s'"),
|
|
|
|
vol->target.path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (close(fd) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot close file '%s'"),
|
|
|
|
vol->target.path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
fd = -1;
|
|
|
|
|
|
|
|
/* Fill in data about this new vol */
|
|
|
|
if (virStorageBackendLogicalFindLVs(conn, pool, vol) < 0) {
|
2009-01-20 17:13:33 +00:00
|
|
|
virReportSystemError(conn, errno,
|
|
|
|
_("cannot find newly created volume '%s'"),
|
|
|
|
vol->target.path);
|
2008-02-20 15:45:33 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (fd != -1)
|
|
|
|
close(fd);
|
|
|
|
virStorageBackendLogicalDeleteVol(conn, pool, vol, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virStorageBackendLogicalDeleteVol(virConnectPtr conn,
|
|
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
|
|
virStorageVolDefPtr vol,
|
|
|
|
unsigned int flags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
const char *cmdargv[] = {
|
|
|
|
LVREMOVE, "-f", vol->target.path, NULL
|
|
|
|
};
|
|
|
|
|
2008-08-08 15:43:38 +00:00
|
|
|
if (virRun(conn, cmdargv, NULL) < 0)
|
2008-02-20 15:45:33 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virStorageBackend virStorageBackendLogical = {
|
|
|
|
.type = VIR_STORAGE_POOL_LOGICAL,
|
|
|
|
|
2008-08-27 20:05:58 +00:00
|
|
|
.findPoolSources = virStorageBackendLogicalFindPoolSources,
|
2008-02-20 15:45:33 +00:00
|
|
|
.startPool = virStorageBackendLogicalStartPool,
|
|
|
|
.buildPool = virStorageBackendLogicalBuildPool,
|
|
|
|
.refreshPool = virStorageBackendLogicalRefreshPool,
|
|
|
|
.stopPool = virStorageBackendLogicalStopPool,
|
|
|
|
.deletePool = virStorageBackendLogicalDeletePool,
|
|
|
|
.createVol = virStorageBackendLogicalCreateVol,
|
|
|
|
.deleteVol = virStorageBackendLogicalDeleteVol,
|
|
|
|
};
|