storage_backend_rbd.c 18.3 KB
Newer Older
1 2 3
/*
 * storage_backend_rbd.c: storage backend for RBD (RADOS Block Device) handling
 *
4
 * Copyright (C) 2013 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * Copyright (C) 2012 Wido den Hollander
 *
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25
 *
 * Author: Wido den Hollander <wido@widodh.nl>
 */

#include <config.h>

26
#include "datatypes.h"
27
#include "virerror.h"
28 29
#include "storage_backend_rbd.h"
#include "storage_conf.h"
30
#include "viralloc.h"
31
#include "virlog.h"
32
#include "base64.h"
33
#include "viruuid.h"
34
#include "virstring.h"
35 36 37 38 39 40 41 42 43 44 45 46
#include "rados/librados.h"
#include "rbd/librbd.h"

#define VIR_FROM_THIS VIR_FROM_STORAGE

struct _virStorageBackendRBDState {
    rados_t cluster;
    rados_ioctx_t ioctx;
    time_t starttime;
};

typedef struct _virStorageBackendRBDState virStorageBackendRBDState;
E
Eric Blake 已提交
47
typedef virStorageBackendRBDState *virStorageBackendRBDStatePtr;
48

E
Eric Blake 已提交
49
static int virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr ptr,
50 51 52 53 54 55 56 57 58 59
                                             virConnectPtr conn,
                                             virStoragePoolObjPtr pool)
{
    int ret = -1;
    unsigned char *secret_value = NULL;
    size_t secret_value_size;
    char *rados_key = NULL;
    virBuffer mon_host = VIR_BUFFER_INITIALIZER;
    virSecretPtr secret = NULL;
    char secretUuid[VIR_UUID_STRING_BUFLEN];
60
    size_t i;
61 62 63 64 65 66 67 68 69
    char *mon_buff = NULL;

    VIR_DEBUG("Found Cephx username: %s",
              pool->def->source.auth.cephx.username);

    if (pool->def->source.auth.cephx.username != NULL) {
        VIR_DEBUG("Using cephx authorization");
        if (rados_create(&ptr->cluster,
            pool->def->source.auth.cephx.username) < 0) {
70
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
71
                           _("failed to initialize RADOS"));
72 73 74
            goto cleanup;
        }

75 76 77 78 79 80 81
        if (!conn) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("'ceph' authentication not supported "
                             "for autostarted pools"));
            return -1;
        }

82
        if (pool->def->source.auth.cephx.secret.uuidUsable) {
83 84 85
            virUUIDFormat(pool->def->source.auth.cephx.secret.uuid, secretUuid);
            VIR_DEBUG("Looking up secret by UUID: %s", secretUuid);
            secret = virSecretLookupByUUIDString(conn, secretUuid);
86
        } else if (pool->def->source.auth.cephx.secret.usage != NULL) {
87 88 89 90 91 92 93
            VIR_DEBUG("Looking up secret by usage: %s",
                      pool->def->source.auth.cephx.secret.usage);
            secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_CEPH,
                                            pool->def->source.auth.cephx.secret.usage);
        }

        if (secret == NULL) {
94 95 96
            if (pool->def->source.auth.cephx.secret.uuidUsable) {
                virReportError(VIR_ERR_NO_SECRET,
                               _("no secret matches uuid '%s'"),
97
                                 secretUuid);
98 99 100 101 102
            } else {
                virReportError(VIR_ERR_NO_SECRET,
                               _("no secret matches usage value '%s'"),
                                 pool->def->source.auth.cephx.secret.usage);
            }
103 104 105
            goto cleanup;
        }

106 107 108 109
        secret_value = conn->secretDriver->secretGetValue(secret, &secret_value_size, 0,
                                                          VIR_SECRET_GET_VALUE_INTERNAL_CALL);

        if (!secret_value) {
110 111 112 113 114
            if (pool->def->source.auth.cephx.secret.uuidUsable) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username '%s' using uuid '%s'"),
                               pool->def->source.auth.cephx.username,
115
                               secretUuid);
116 117 118 119 120 121 122
            } else {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username '%s' using usage value '%s'"),
                               pool->def->source.auth.cephx.username,
                               pool->def->source.auth.cephx.secret.usage);
            }
123 124 125
            goto cleanup;
        }

126 127 128 129 130
        base64_encode_alloc((char *)secret_value,
                            secret_value_size, &rados_key);
        memset(secret_value, 0, secret_value_size);

        if (rados_key == NULL) {
131
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
132
                           _("failed to decode the RADOS key"));
133 134 135 136 137
            goto cleanup;
        }

        VIR_DEBUG("Found cephx key: %s", rados_key);
        if (rados_conf_set(ptr->cluster, "key", rados_key) < 0) {
138 139 140
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("failed to set RADOS option: %s"),
                           "rados_key");
141 142 143 144 145 146
            goto cleanup;
        }

        memset(rados_key, 0, strlen(rados_key));

        if (rados_conf_set(ptr->cluster, "auth_supported", "cephx") < 0) {
147 148 149
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("failed to set RADOS option: %s"),
                           "auth_supported");
150 151 152 153 154
            goto cleanup;
        }
    } else {
        VIR_DEBUG("Not using cephx authorization");
        if (rados_create(&ptr->cluster, NULL) < 0) {
155
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
156
                           _("failed to create the RADOS cluster"));
157 158 159
            goto cleanup;
        }
        if (rados_conf_set(ptr->cluster, "auth_supported", "none") < 0) {
160 161 162
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("failed to set RADOS option: %s"),
                           "auth_supported");
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
            goto cleanup;
        }
    }

    VIR_DEBUG("Found %zu RADOS cluster monitors in the pool configuration",
              pool->def->source.nhost);

    for (i = 0; i < pool->def->source.nhost; i++) {
        if (pool->def->source.hosts[i].name != NULL &&
            !pool->def->source.hosts[i].port) {
            virBufferAsprintf(&mon_host, "%s:6789,",
                              pool->def->source.hosts[i].name);
        } else if (pool->def->source.hosts[i].name != NULL &&
            pool->def->source.hosts[i].port) {
            virBufferAsprintf(&mon_host, "%s:%d,",
                              pool->def->source.hosts[i].name,
                              pool->def->source.hosts[i].port);
        } else {
181 182
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("received malformed monitor, check the XML definition"));
183 184 185 186 187 188 189 190 191 192 193
        }
    }

    if (virBufferError(&mon_host)) {
       virReportOOMError();
       goto cleanup;
    }

    mon_buff = virBufferContentAndReset(&mon_host);
    VIR_DEBUG("RADOS mon_host has been set to: %s", mon_buff);
    if (rados_conf_set(ptr->cluster, "mon_host", mon_buff) < 0) {
194 195 196
       virReportError(VIR_ERR_INTERNAL_ERROR,
                      _("failed to set RADOS option: %s"),
                      "mon_host");
197 198 199 200 201
        goto cleanup;
    }

    ptr->starttime = time(0);
    if (rados_connect(ptr->cluster) < 0) {
202 203 204
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to connect to the RADOS monitor on: %s"),
                       mon_buff);
205 206 207 208 209 210 211 212
        goto cleanup;
    }

    ret = 0;

cleanup:
    VIR_FREE(secret_value);
    VIR_FREE(rados_key);
213 214 215 216

    if (secret != NULL)
        virSecretFree(secret);

217 218 219 220 221 222 223 224 225
    virBufferFreeAndReset(&mon_host);
    VIR_FREE(mon_buff);
    return ret;
}

static int virStorageBackendRBDCloseRADOSConn(virStorageBackendRBDStatePtr ptr)
{
    int ret = 0;

E
Eric Blake 已提交
226
    if (ptr->ioctx != NULL) {
227
        VIR_DEBUG("Closing RADOS IoCTX");
E
Eric Blake 已提交
228
        rados_ioctx_destroy(ptr->ioctx);
229 230
        ret = -1;
    }
E
Eric Blake 已提交
231
    ptr->ioctx = NULL;
232

E
Eric Blake 已提交
233
    if (ptr->cluster != NULL) {
234
        VIR_DEBUG("Closing RADOS connection");
E
Eric Blake 已提交
235
        rados_shutdown(ptr->cluster);
236 237
        ret = -2;
    }
E
Eric Blake 已提交
238
    ptr->cluster = NULL;
239

E
Eric Blake 已提交
240
    time_t runtime = time(0) - ptr->starttime;
241 242 243 244 245 246 247 248 249 250 251
    VIR_DEBUG("RADOS connection existed for %ld seconds", runtime);

    return ret;
}

static int volStorageBackendRBDRefreshVolInfo(virStorageVolDefPtr vol,
                                              virStoragePoolObjPtr pool,
                                              virStorageBackendRBDStatePtr ptr)
{
    int ret = -1;
    rbd_image_t image;
E
Eric Blake 已提交
252
    if (rbd_open(ptr->ioctx, vol->name, &image, NULL) < 0) {
253 254 255
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to open the RBD image '%s'"),
                       vol->name);
256 257 258 259 260
        return ret;
    }

    rbd_image_info_t info;
    if (rbd_stat(image, &info, sizeof(info)) < 0) {
261 262
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("failed to stat the RBD image"));
263 264 265 266 267 268 269 270 271 272 273 274 275
        goto cleanup;
    }

    VIR_DEBUG("Refreshed RBD image %s/%s (size: %llu obj_size: %llu num_objs: %llu)",
              pool->def->source.name, vol->name, (unsigned long long)info.size,
              (unsigned long long)info.obj_size,
              (unsigned long long)info.num_objs);

    vol->capacity = info.size;
    vol->allocation = info.obj_size * info.num_objs;
    vol->type = VIR_STORAGE_VOL_NETWORK;

    VIR_FREE(vol->target.path);
276
    if (virAsprintf(&vol->target.path, "%s/%s",
277
                    pool->def->source.name,
278
                    vol->name) == -1)
279 280 281 282 283
        goto cleanup;

    VIR_FREE(vol->key);
    if (virAsprintf(&vol->key, "%s/%s",
                    pool->def->source.name,
284
                    vol->name) == -1)
285 286 287 288 289 290 291 292 293
        goto cleanup;

    ret = 0;

cleanup:
    rbd_close(image);
    return ret;
}

294
static int virStorageBackendRBDRefreshPool(virConnectPtr conn,
295 296 297 298 299 300
                                           virStoragePoolObjPtr pool)
{
    size_t max_size = 1024;
    int ret = -1;
    int len = -1;
    char *name, *names = NULL;
E
Eric Blake 已提交
301
    virStorageBackendRBDState ptr;
302 303 304 305 306 307 308 309 310
    ptr.cluster = NULL;
    ptr.ioctx = NULL;

    if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
        goto cleanup;
    }

    if (rados_ioctx_create(ptr.cluster,
        pool->def->source.name, &ptr.ioctx) < 0) {
311 312 313
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
                       pool->def->source.name);
314 315 316 317 318
        goto cleanup;
    }

    struct rados_cluster_stat_t stat;
    if (rados_cluster_stat(ptr.cluster, &stat) < 0) {
319 320
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("failed to stat the RADOS cluster"));
321 322 323 324 325
        goto cleanup;
    }

    struct rados_pool_stat_t poolstat;
    if (rados_ioctx_pool_stat(ptr.ioctx, &poolstat) < 0) {
326 327 328
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to stat the RADOS pool '%s'"),
                       pool->def->source.name);
329 330 331 332 333 334 335 336 337 338 339 340 341 342
        goto cleanup;
    }

    pool->def->capacity = stat.kb * 1024;
    pool->def->available = stat.kb_avail * 1024;
    pool->def->allocation = poolstat.num_bytes;

    VIR_DEBUG("Utilization of RBD pool %s: (kb: %llu kb_avail: %llu num_bytes: %llu)",
              pool->def->source.name, (unsigned long long)stat.kb,
              (unsigned long long)stat.kb_avail,
              (unsigned long long)poolstat.num_bytes);

    while (true) {
        if (VIR_ALLOC_N(names, max_size) < 0)
343
            goto cleanup;
344 345 346 347 348

        len = rbd_list(ptr.ioctx, names, &max_size);
        if (len >= 0)
            break;
        if (len != -ERANGE) {
J
Ján Tomko 已提交
349
            VIR_WARN("%s", _("A problem occurred while listing RBD images"));
350 351
            goto cleanup;
        }
352
        VIR_FREE(names);
353 354
    }

355
    for (name = names; name < names + max_size;) {
356 357
        virStorageVolDefPtr vol;

358 359
        if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) {
            virStoragePoolObjClearVols(pool);
360
            goto cleanup;
361 362
        }

363 364 365
        if (STREQ(name, ""))
            break;

366
        if (VIR_ALLOC(vol) < 0)
367
            goto cleanup;
368

369
        if (VIR_STRDUP(vol->name, name) < 0) {
370
            VIR_FREE(vol);
371
            goto cleanup;
372
        }
373 374 375

        name += strlen(name) + 1;

E
Eric Blake 已提交
376
        if (volStorageBackendRBDRefreshVolInfo(vol, pool, &ptr) < 0) {
377
            virStorageVolDefFree(vol);
378
            goto cleanup;
379
        }
380 381 382 383

        pool->volumes.objs[pool->volumes.count++] = vol;
    }

384
    VIR_DEBUG("Found %zu images in RBD pool %s",
385 386 387 388 389 390
              pool->volumes.count, pool->def->source.name);

    ret = 0;

cleanup:
    VIR_FREE(names);
E
Eric Blake 已提交
391
    virStorageBackendRBDCloseRADOSConn(&ptr);
392 393 394 395 396 397 398 399 400
    return ret;
}

static int virStorageBackendRBDDeleteVol(virConnectPtr conn,
                                         virStoragePoolObjPtr pool,
                                         virStorageVolDefPtr vol,
                                         unsigned int flags)
{
    int ret = -1;
E
Eric Blake 已提交
401
    virStorageBackendRBDState ptr;
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
    ptr.cluster = NULL;
    ptr.ioctx = NULL;

    VIR_DEBUG("Removing RBD image %s/%s", pool->def->source.name, vol->name);

    if (flags & VIR_STORAGE_VOL_DELETE_ZEROED) {
        VIR_WARN("%s", _("This storage backend does not supported zeroed removal of volumes"));
    }

    if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
        goto cleanup;
    }

    if (rados_ioctx_create(ptr.cluster,
        pool->def->source.name, &ptr.ioctx) < 0) {
417 418 419
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
                       pool->def->source.name);
420 421 422 423
        goto cleanup;
    }

    if (rbd_remove(ptr.ioctx, vol->name) < 0) {
424 425 426 427
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to remove volume '%s/%s'"),
                       pool->def->source.name,
                       vol->name);
428 429 430 431 432 433
        goto cleanup;
    }

    ret = 0;

cleanup:
E
Eric Blake 已提交
434
    virStorageBackendRBDCloseRADOSConn(&ptr);
435 436 437 438 439 440 441
    return ret;
}

static int virStorageBackendRBDCreateVol(virConnectPtr conn,
                                         virStoragePoolObjPtr pool,
                                         virStorageVolDefPtr vol)
{
E
Eric Blake 已提交
442
    virStorageBackendRBDState ptr;
443 444 445 446 447 448 449 450 451 452 453 454 455 456
    ptr.cluster = NULL;
    ptr.ioctx = NULL;
    int order = 0;
    int ret = -1;

    VIR_DEBUG("Creating RBD image %s/%s with size %llu",
              pool->def->source.name,
              vol->name, vol->capacity);

    if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
        goto cleanup;
    }

    if (rados_ioctx_create(ptr.cluster,
457
                           pool->def->source.name, &ptr.ioctx) < 0) {
458 459 460
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
                       pool->def->source.name);
461 462 463 464
        goto cleanup;
    }

    if (vol->target.encryption != NULL) {
465 466
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("storage pool does not support encrypted volumes"));
467 468 469 470
        goto cleanup;
    }

    if (rbd_create(ptr.ioctx, vol->name, vol->capacity, &order) < 0) {
471 472 473 474
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create volume '%s/%s'"),
                       pool->def->source.name,
                       vol->name);
475 476 477
        goto cleanup;
    }

E
Eric Blake 已提交
478
    if (volStorageBackendRBDRefreshVolInfo(vol, pool, &ptr) < 0) {
479 480 481 482 483 484
        goto cleanup;
    }

    ret = 0;

cleanup:
E
Eric Blake 已提交
485
    virStorageBackendRBDCloseRADOSConn(&ptr);
486 487 488 489 490 491 492
    return ret;
}

static int virStorageBackendRBDRefreshVol(virConnectPtr conn,
                                          virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                          virStorageVolDefPtr vol)
{
E
Eric Blake 已提交
493
    virStorageBackendRBDState ptr;
494 495 496 497 498 499 500 501 502 503
    ptr.cluster = NULL;
    ptr.ioctx = NULL;
    int ret = -1;

    if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
        goto cleanup;
    }

    if (rados_ioctx_create(ptr.cluster,
        pool->def->source.name, &ptr.ioctx) < 0) {
504 505 506
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
                       pool->def->source.name);
507 508 509
        goto cleanup;
    }

E
Eric Blake 已提交
510
    if (volStorageBackendRBDRefreshVolInfo(vol, pool, &ptr) < 0) {
511 512 513 514 515 516
        goto cleanup;
    }

    ret = 0;

cleanup:
E
Eric Blake 已提交
517
    virStorageBackendRBDCloseRADOSConn(&ptr);
518 519 520 521 522 523 524 525 526
    return ret;
}

static int virStorageBackendRBDResizeVol(virConnectPtr conn ATTRIBUTE_UNUSED,
                                     virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                     virStorageVolDefPtr vol,
                                     unsigned long long capacity,
                                     unsigned int flags)
{
E
Eric Blake 已提交
527
    virStorageBackendRBDState ptr;
528 529 530 531 532 533 534 535 536 537 538 539 540
    ptr.cluster = NULL;
    ptr.ioctx = NULL;
    rbd_image_t image = NULL;
    int ret = -1;

    virCheckFlags(0, -1);

    if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) {
        goto cleanup;
    }

    if (rados_ioctx_create(ptr.cluster,
        pool->def->source.name, &ptr.ioctx) < 0) {
541 542 543
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to create the RBD IoCTX. Does the pool '%s' exist?"),
                       pool->def->source.name);
544 545 546 547
        goto cleanup;
    }

    if (rbd_open(ptr.ioctx, vol->name, &image, NULL) < 0) {
548 549 550
       virReportError(VIR_ERR_INTERNAL_ERROR,
                      _("failed to open the RBD image '%s'"),
                      vol->name);
551 552 553 554
       goto cleanup;
    }

    if (rbd_resize(image, capacity) < 0) {
555 556 557
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to resize the RBD image '%s'"),
                       vol->name);
558 559 560 561 562 563 564 565
        goto cleanup;
    }

    ret = 0;

cleanup:
    if (image != NULL)
       rbd_close(image);
E
Eric Blake 已提交
566
    virStorageBackendRBDCloseRADOSConn(&ptr);
567 568 569 570 571 572 573 574 575 576 577 578
    return ret;
}

virStorageBackend virStorageBackendRBD = {
    .type = VIR_STORAGE_POOL_RBD,

    .refreshPool = virStorageBackendRBDRefreshPool,
    .createVol = virStorageBackendRBDCreateVol,
    .refreshVol = virStorageBackendRBDRefreshVol,
    .deleteVol = virStorageBackendRBDDeleteVol,
    .resizeVol = virStorageBackendRBDResizeVol,
};