storage_backend_zfs.c 12.5 KB
Newer Older
R
Roman Bogorodskiy 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * storage_backend_zfs.c: storage backend for ZFS handling
 *
 * Copyright (C) 2014 Roman Bogorodskiy
 *
 * 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, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "storage_backend_zfs.h"
#include "virlog.h"
#include "virstring.h"
30
#include "storage_util.h"
R
Roman Bogorodskiy 已提交
31 32 33 34 35 36 37 38 39 40 41 42

#define VIR_FROM_THIS VIR_FROM_STORAGE

VIR_LOG_INIT("storage.storage_backend_zfs");

/*
 * Some common flags of zfs and zpool commands we use:
 * -H -- don't print headers and separate fields by tab
 * -p -- show exact numbers instead of human-readable, i.e.
 *       for size, show just a number instead of 2G etc
 */

43 44 45 46 47 48 49 50 51 52 53
/**
 * virStorageBackendZFSVolModeNeeded:
 *
 * Checks if it's necessary to specify 'volmode' (i.e. that
 * we're working with BSD ZFS implementation).
 *
 * Returns 1 if 'volmode' is need, 0 if not needed, -1 on error
 */
static int
virStorageBackendZFSVolModeNeeded(void)
{
54
    int ret = -1, exit_code = -1;
55
    g_autofree char *error = NULL;
J
Ján Tomko 已提交
56
    g_autoptr(virCommand) cmd = NULL;
57 58 59 60 61 62 63 64 65 66

    /* 'zfs get' without arguments prints out
     * usage information to stderr, including
     * list of supported options, and exits with
     * exit code 2
     */
    cmd = virCommandNewArgList(ZFS, "get", NULL);
    virCommandAddEnvString(cmd, "LC_ALL=C");
    virCommandSetErrorBuffer(cmd, &error);

67 68
    ret = virCommandRun(cmd, &exit_code);
    if ((ret < 0) || (exit_code != 2)) {
69 70 71 72 73 74 75 76 77 78 79 80 81
        VIR_WARN("Command 'zfs get' either failed "
                 "to run or exited with unexpected status");
        goto cleanup;
    }

    if (strstr(error, " volmode "))
        ret = 1;
    else
        ret = 0;

 cleanup:
    return ret;
}
R
Roman Bogorodskiy 已提交
82 83

static int
J
Ján Tomko 已提交
84
virStorageBackendZFSCheckPool(virStoragePoolObjPtr pool G_GNUC_UNUSED,
R
Roman Bogorodskiy 已提交
85 86
                              bool *isActive)
{
87
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
88
    g_autofree char *devpath = NULL;
R
Roman Bogorodskiy 已提交
89

90
    devpath = g_strdup_printf("/dev/zvol/%s", def->source.name);
R
Roman Bogorodskiy 已提交
91 92 93 94 95 96 97
    *isActive = virFileIsDir(devpath);

    return 0;
}

static int
virStorageBackendZFSParseVol(virStoragePoolObjPtr pool,
98 99
                             virStorageVolDefPtr vol,
                             const char *volume_string)
R
Roman Bogorodskiy 已提交
100 101 102 103 104
{
    int ret = -1;
    size_t count;
    char *vol_name;
    bool is_new_vol = false;
105
    virStorageVolDefPtr volume = NULL;
106
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
107 108
    VIR_AUTOSTRINGLIST tokens = NULL;
    VIR_AUTOSTRINGLIST name_tokens = NULL;
R
Roman Bogorodskiy 已提交
109

110
    if (!(tokens = virStringSplitCount(volume_string, "\t", 0, &count)))
R
Roman Bogorodskiy 已提交
111 112
        return -1;

R
Richard Laager 已提交
113
    if (count != 3)
R
Roman Bogorodskiy 已提交
114 115 116 117 118 119 120
        goto cleanup;

    if (!(name_tokens = virStringSplit(tokens[0], "/", 2)))
        goto cleanup;

    vol_name = name_tokens[1];

121 122 123 124
    if (vol == NULL)
        volume = virStorageVolDefFindByName(pool, vol_name);
    else
        volume = vol;
R
Roman Bogorodskiy 已提交
125

126 127
    if (volume == NULL) {
        if (VIR_ALLOC(volume) < 0)
R
Roman Bogorodskiy 已提交
128 129 130
            goto cleanup;

        is_new_vol = true;
131
        volume->type = VIR_STORAGE_VOL_BLOCK;
R
Roman Bogorodskiy 已提交
132

133
        volume->name = g_strdup(vol_name);
R
Roman Bogorodskiy 已提交
134 135
    }

136 137
    if (!volume->key)
        volume->key = g_strdup(tokens[0]);
R
Roman Bogorodskiy 已提交
138

139
    if (volume->target.path == NULL) {
140 141
        volume->target.path = g_strdup_printf("%s/%s", def->target.path,
                                              volume->name);
R
Roman Bogorodskiy 已提交
142 143
    }

144
    if (virStrToLong_ull(tokens[1], NULL, 10, &volume->target.capacity) < 0) {
R
Roman Bogorodskiy 已提交
145 146 147 148 149
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("malformed volsize reported"));
        goto cleanup;
    }

R
Richard Laager 已提交
150 151 152 153 154 155 156 157 158
    if (virStrToLong_ull(tokens[2], NULL, 10, &volume->target.allocation) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("malformed refreservation reported"));
        goto cleanup;
    }

    if (volume->target.allocation < volume->target.capacity)
        volume->target.sparse = true;

159
    if (is_new_vol && virStoragePoolObjAddVol(pool, volume) < 0)
R
Roman Bogorodskiy 已提交
160
        goto cleanup;
161
    volume = NULL;
R
Roman Bogorodskiy 已提交
162 163 164

    ret = 0;
 cleanup:
165
    if (is_new_vol)
166
        virStorageVolDefFree(volume);
R
Roman Bogorodskiy 已提交
167 168 169 170
    return ret;
}

static int
171 172
virStorageBackendZFSFindVols(virStoragePoolObjPtr pool,
                             virStorageVolDefPtr vol)
R
Roman Bogorodskiy 已提交
173
{
174
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
R
Roman Bogorodskiy 已提交
175
    size_t i;
176
    VIR_AUTOSTRINGLIST lines = NULL;
J
Ján Tomko 已提交
177
    g_autoptr(virCommand) cmd = NULL;
178
    g_autofree char *volumes_list = NULL;
R
Roman Bogorodskiy 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194

    /**
     * $ zfs list -Hp -t volume -o name,volsize -r test
     * test/vol1       5368709120
     * test/vol3       1073741824
     * test/vol4       1572864000
     * $
     *
     * Arguments description:
     *  -t volume -- we want to see only volumes
     *  -o name,volsize -- limit output to name and volume size
     *  -r -- we want to see all the childer of our pool
     */
    cmd = virCommandNewArgList(ZFS,
                               "list", "-Hp",
                               "-t", "volume", "-r",
R
Richard Laager 已提交
195
                               "-o", "name,volsize,refreservation",
196
                               def->source.name,
R
Roman Bogorodskiy 已提交
197 198 199
                               NULL);
    virCommandSetOutputBuffer(cmd, &volumes_list);
    if (virCommandRun(cmd, NULL) < 0)
200
        return -1;
R
Roman Bogorodskiy 已提交
201 202

    if (!(lines = virStringSplit(volumes_list, "\n", 0)))
203
        return -1;
R
Roman Bogorodskiy 已提交
204 205 206 207 208

    for (i = 0; lines[i]; i++) {
        if (STREQ(lines[i], ""))
            continue;

209
        if (virStorageBackendZFSParseVol(pool, vol, lines[i]) < 0)
R
Roman Bogorodskiy 已提交
210 211 212 213 214 215 216
            continue;
    }

    return 0;
}

static int
J
Ján Tomko 已提交
217
virStorageBackendZFSRefreshPool(virStoragePoolObjPtr pool G_GNUC_UNUSED)
R
Roman Bogorodskiy 已提交
218
{
219
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
R
Roman Bogorodskiy 已提交
220 221
    char *zpool_props = NULL;
    size_t i;
J
Ján Tomko 已提交
222
    g_autoptr(virCommand) cmd = NULL;
223 224
    VIR_AUTOSTRINGLIST lines = NULL;
    VIR_AUTOSTRINGLIST tokens = NULL;
R
Roman Bogorodskiy 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238

    /**
     * $ zpool get -Hp health,size,free,allocated test
     * test    health  ONLINE  -
     * test    size    199715979264    -
     * test    free    198899976704    -
     * test    allocated       816002560       -
     * $
     *
     * Here we just provide a list of properties we want to see
     */
    cmd = virCommandNewArgList(ZPOOL,
                               "get", "-Hp",
                               "health,size,free,allocated",
239
                               def->source.name,
R
Roman Bogorodskiy 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
                               NULL);
    virCommandSetOutputBuffer(cmd, &zpool_props);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

    if (!(lines = virStringSplit(zpool_props, "\n", 0)))
        goto cleanup;

    for (i = 0; lines[i]; i++) {
        size_t count;
        char *prop_name;

        if (STREQ(lines[i], ""))
            continue;

255
        virStringListFree(tokens);
R
Roman Bogorodskiy 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
        if (!(tokens = virStringSplitCount(lines[i], "\t", 0, &count)))
            goto cleanup;

        if (count != 4)
            continue;

        prop_name = tokens[1];

        if (STREQ(prop_name, "free") || STREQ(prop_name, "size") ||
            STREQ(prop_name, "allocated")) {
            unsigned long long value;
            if (virStrToLong_ull(tokens[2], NULL, 10, &value) < 0)
                goto cleanup;

            if (STREQ(prop_name, "free"))
271
                def->available = value;
R
Roman Bogorodskiy 已提交
272
            else if (STREQ(prop_name, "size"))
273
                def->capacity = value;
R
Roman Bogorodskiy 已提交
274
            else if (STREQ(prop_name, "allocated"))
275
                def->allocation = value;
R
Roman Bogorodskiy 已提交
276 277 278 279
        }
    }

    /* Obtain a list of volumes */
280
    if (virStorageBackendZFSFindVols(pool, NULL) < 0)
R
Roman Bogorodskiy 已提交
281 282 283 284 285 286 287 288 289
        goto cleanup;

 cleanup:
    VIR_FREE(zpool_props);

    return 0;
}

static int
290
virStorageBackendZFSCreateVol(virStoragePoolObjPtr pool,
R
Roman Bogorodskiy 已提交
291 292
                              virStorageVolDefPtr vol)
{
293
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
R
Roman Bogorodskiy 已提交
294
    int ret = -1;
295
    int volmode_needed = -1;
J
Ján Tomko 已提交
296
    g_autoptr(virCommand) cmd = NULL;
R
Roman Bogorodskiy 已提交
297

298 299 300 301 302 303 304
    if (vol->target.encryption != NULL) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       "%s", _("storage pool does not support encrypted "
                               "volumes"));
        return -1;
    }

R
Roman Bogorodskiy 已提交
305 306
    vol->type = VIR_STORAGE_VOL_BLOCK;

307
    VIR_FREE(vol->target.path);
308
    vol->target.path = g_strdup_printf("%s/%s", def->target.path, vol->name);
R
Roman Bogorodskiy 已提交
309

310
    vol->key = g_strdup(vol->target.path);
R
Roman Bogorodskiy 已提交
311

312 313 314
    volmode_needed = virStorageBackendZFSVolModeNeeded();
    if (volmode_needed < 0)
        goto cleanup;
R
Roman Bogorodskiy 已提交
315 316
    /**
     * $ zfs create -o volmode=dev -V 10240K test/volname
R
Richard Laager 已提交
317 318
     * $ zfs create -o volmode=dev -s -V 10240K test/volname
     * $ zfs create -o volmode=dev -s -o refreservation=1024K -V 10240K test/volname
R
Roman Bogorodskiy 已提交
319 320 321 322
     *
     * -o volmode=dev -- we want to get volumes exposed as cdev
     *                   devices. If we don't specify that zfs
     *                   will lookup vfs.zfs.vol.mode sysctl value
R
Richard Laager 已提交
323 324
     * -s -- create a sparse volume
     * -o refreservation -- reserve the specified amount of space
R
Roman Bogorodskiy 已提交
325 326
     * -V -- tells to create a volume with the specified size
     */
327 328 329
    cmd = virCommandNewArgList(ZFS, "create", NULL);
    if (volmode_needed)
        virCommandAddArgList(cmd, "-o", "volmode=dev", NULL);
R
Richard Laager 已提交
330 331 332 333 334 335 336 337 338
    if (vol->target.capacity != vol->target.allocation) {
        virCommandAddArg(cmd, "-s");
        if (vol->target.allocation > 0) {
            virCommandAddArg(cmd, "-o");
            virCommandAddArgFormat(cmd, "refreservation=%lluK",
                                   VIR_DIV_UP(vol->target.allocation, 1024));
        }
        vol->target.sparse = true;
    }
339
    virCommandAddArg(cmd, "-V");
R
Roman Bogorodskiy 已提交
340 341
    virCommandAddArgFormat(cmd, "%lluK",
                           VIR_DIV_UP(vol->target.capacity, 1024));
342
    virCommandAddArgFormat(cmd, "%s/%s", def->source.name, vol->name);
R
Roman Bogorodskiy 已提交
343 344 345 346

    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

347
    if (virStorageBackendZFSFindVols(pool, vol) < 0)
R
Roman Bogorodskiy 已提交
348 349 350 351 352 353 354 355 356
        goto cleanup;

    ret = 0;
 cleanup:
    return ret;

}

static int
357
virStorageBackendZFSDeleteVol(virStoragePoolObjPtr pool,
R
Roman Bogorodskiy 已提交
358 359 360
                              virStorageVolDefPtr vol,
                              unsigned int flags)
{
361
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
J
Ján Tomko 已提交
362
    g_autoptr(virCommand) destroy_cmd = NULL;
R
Roman Bogorodskiy 已提交
363 364 365

    virCheckFlags(0, -1);

J
John Ferlan 已提交
366 367
    destroy_cmd = virCommandNewArgList(ZFS, "destroy", NULL);

R
Roman Bogorodskiy 已提交
368
    virCommandAddArgFormat(destroy_cmd, "%s/%s",
369
                           def->source.name, vol->name);
R
Roman Bogorodskiy 已提交
370

371
    return virCommandRun(destroy_cmd, NULL);
R
Roman Bogorodskiy 已提交
372 373
}

374
static int
375
virStorageBackendZFSBuildPool(virStoragePoolObjPtr pool,
376 377
                              unsigned int flags)
{
378
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
379
    size_t i;
J
Ján Tomko 已提交
380
    g_autoptr(virCommand) cmd = NULL;
381
    int ret = -1;
382 383 384

    virCheckFlags(0, -1);

385
    if (def->source.ndevice == 0) {
386 387 388 389 390 391
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       "%s", _("missing source devices"));
        return -1;
    }

    cmd = virCommandNewArgList(ZPOOL, "create",
392
                               def->source.name, NULL);
393

394 395
    for (i = 0; i < def->source.ndevice; i++)
        virCommandAddArg(cmd, def->source.devices[i].path);
396

397 398 399 400 401
    virObjectUnlock(pool);
    ret = virCommandRun(cmd, NULL);
    virObjectLock(pool);

    return ret;
402 403 404
}

static int
405
virStorageBackendZFSDeletePool(virStoragePoolObjPtr pool,
406 407
                               unsigned int flags)
{
408
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
J
Ján Tomko 已提交
409
    g_autoptr(virCommand) cmd = NULL;
410 411 412 413

    virCheckFlags(0, -1);

    cmd = virCommandNewArgList(ZPOOL, "destroy",
414
                               def->source.name, NULL);
415

416
    return virCommandRun(cmd, NULL);
417
}
R
Roman Bogorodskiy 已提交
418 419 420 421 422 423 424 425

virStorageBackend virStorageBackendZFS = {
    .type = VIR_STORAGE_POOL_ZFS,

    .checkPool = virStorageBackendZFSCheckPool,
    .refreshPool = virStorageBackendZFSRefreshPool,
    .createVol = virStorageBackendZFSCreateVol,
    .deleteVol = virStorageBackendZFSDeleteVol,
426 427
    .buildPool = virStorageBackendZFSBuildPool,
    .deletePool = virStorageBackendZFSDeletePool,
428 429
    .uploadVol = virStorageBackendVolUploadLocal,
    .downloadVol = virStorageBackendVolDownloadLocal,
R
Roman Bogorodskiy 已提交
430
};
431 432 433 434 435 436 437


int
virStorageBackendZFSRegister(void)
{
    return virStorageBackendRegister(&virStorageBackendZFS);
}