/* * virsh-pool.c: Commands to manage storage pool * * Copyright (C) 2005, 2007-2016 Red Hat, Inc. * * 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 * . */ #include #include "virsh-pool.h" #include "virsh-util.h" #include "internal.h" #include "virbuffer.h" #include "viralloc.h" #include "virfile.h" #include "conf/storage_conf.h" #include "virtime.h" #include "vsh-table.h" #include "virenum.h" #define VIRSH_COMMON_OPT_POOL_FULL(cflags) \ VIRSH_COMMON_OPT_POOL(N_("pool name or uuid"), cflags) #define VIRSH_COMMON_OPT_POOL_BUILD \ {.name = "build", \ .type = VSH_OT_BOOL, \ .flags = 0, \ .help = N_("build the pool as normal") \ } #define VIRSH_COMMON_OPT_POOL_NO_OVERWRITE \ {.name = "no-overwrite", \ .type = VSH_OT_BOOL, \ .flags = 0, \ .help = N_("do not overwrite any existing data") \ } #define VIRSH_COMMON_OPT_POOL_OVERWRITE \ {.name = "overwrite", \ .type = VSH_OT_BOOL, \ .flags = 0, \ .help = N_("overwrite any existing data") \ } #define VIRSH_COMMON_OPT_POOL_X_AS \ {.name = "name", \ .type = VSH_OT_STRING, \ .positional = true, \ .required = true, \ .completer = virshCompleteEmpty, \ .help = N_("name of the pool") \ }, \ {.name = "type", \ .type = VSH_OT_STRING, \ .positional = true, \ .required = true, \ .completer = virshPoolTypeCompleter, \ .help = N_("type of the pool") \ }, \ {.name = "print-xml", \ .type = VSH_OT_BOOL, \ .help = N_("print XML document, but don't define/create") \ }, \ {.name = "source-host", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .completer = virshCompleteEmpty, \ .help = N_("source-host for underlying storage") \ }, \ {.name = "source-path", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("source path for underlying storage") \ }, \ {.name = "source-dev", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("source device for underlying storage") \ }, \ {.name = "source-name", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("source name for underlying storage") \ }, \ {.name = "target", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("target for underlying storage") \ }, \ {.name = "source-format", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("format for underlying storage") \ }, \ {.name = "auth-type", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("auth type to be used for underlying storage") \ }, \ {.name = "auth-username", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .completer = virshCompleteEmpty, \ .help = N_("auth username to be used for underlying storage") \ }, \ {.name = "secret-usage", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("auth secret usage to be used for underlying storage") \ }, \ {.name = "secret-uuid", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("auth secret UUID to be used for underlying storage") \ }, \ {.name = "adapter-name", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter name to be used for underlying storage") \ }, \ {.name = "adapter-wwnn", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter wwnn to be used for underlying storage") \ }, \ {.name = "adapter-wwpn", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter wwpn to be used for underlying storage") \ }, \ {.name = "adapter-parent", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter parent scsi_hostN to be used for underlying vHBA storage") \ }, \ {.name = "adapter-parent-wwnn", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter parent scsi_hostN wwnn to be used for underlying vHBA storage") \ }, \ {.name = "adapter-parent-wwpn", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter parent scsi_hostN wwpn to be used for underlying vHBA storage") \ }, \ {.name = "adapter-parent-fabric-wwn", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("adapter parent scsi_hostN fabric_wwn to be used for underlying vHBA storage") \ }, \ {.name = "source-protocol-ver", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .help = N_("nfsvers value for NFS pool mount option") \ }, \ {.name = "source-initiator", \ .type = VSH_OT_STRING, \ .unwanted_positional = true, \ .completer = virshCompleteEmpty, \ .help = N_("initiator iqn for underlying storage") \ } virStoragePoolPtr virshCommandOptPoolBy(vshControl *ctl, const vshCmd *cmd, const char *optname, const char **name, unsigned int flags) { virStoragePoolPtr pool = NULL; const char *n = NULL; virshControl *priv = ctl->privData; virCheckFlags(VIRSH_BYUUID | VIRSH_BYNAME, NULL); if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) return NULL; if (cmd->skipChecks && !n) return NULL; vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n", cmd->def->name, optname, n); if (name) *name = n; /* try it by UUID */ if ((flags & VIRSH_BYUUID) && strlen(n) == VIR_UUID_STRING_BUFLEN-1) { vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool UUID\n", cmd->def->name, optname); pool = virStoragePoolLookupByUUIDString(priv->conn, n); } /* try it by NAME */ if (!pool && (flags & VIRSH_BYNAME)) { vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as pool NAME\n", cmd->def->name, optname); pool = virStoragePoolLookupByName(priv->conn, n); } if (!pool) vshError(ctl, _("failed to get pool '%1$s'"), n); return pool; } /* * "pool-autostart" command */ static const vshCmdInfo info_pool_autostart = { .help = N_("autostart a pool"), .desc = N_("Configure a pool to be automatically started at boot."), }; static const vshCmdOptDef opts_pool_autostart[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT), {.name = "disable", .type = VSH_OT_BOOL, .help = N_("disable autostarting") }, {.name = NULL} }; static bool cmdPoolAutostart(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; const char *name; int autostart; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; autostart = !vshCommandOptBool(cmd, "disable"); if (virStoragePoolSetAutostart(pool, autostart) < 0) { if (autostart) vshError(ctl, _("failed to mark pool %1$s as autostarted"), name); else vshError(ctl, _("failed to unmark pool %1$s as autostarted"), name); return false; } if (autostart) vshPrintExtra(ctl, _("Pool %1$s marked as autostarted\n"), name); else vshPrintExtra(ctl, _("Pool %1$s unmarked as autostarted\n"), name); return true; } /* * "pool-create" command */ static const vshCmdInfo info_pool_create = { .help = N_("create a pool from an XML file"), .desc = N_("Create a pool."), }; static const vshCmdOptDef opts_pool_create[] = { VIRSH_COMMON_OPT_FILE(N_("file containing an XML pool description")), VIRSH_COMMON_OPT_POOL_BUILD, VIRSH_COMMON_OPT_POOL_NO_OVERWRITE, VIRSH_COMMON_OPT_POOL_OVERWRITE, {.name = NULL} }; static bool cmdPoolCreate(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; const char *from = NULL; g_autofree char *buffer = NULL; bool build; bool overwrite; bool no_overwrite; unsigned int flags = 0; virshControl *priv = ctl->privData; if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) return false; build = vshCommandOptBool(cmd, "build"); overwrite = vshCommandOptBool(cmd, "overwrite"); no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, "no-overwrite", no_overwrite); if (build) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD; if (overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE; if (no_overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE; if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) return false; if (!(pool = virStoragePoolCreateXML(priv->conn, buffer, flags))) { vshError(ctl, _("Failed to create pool from %1$s"), from); return false; } vshPrintExtra(ctl, _("Pool %1$s created from %2$s\n"), virStoragePoolGetName(pool), from); return true; } static const vshCmdOptDef opts_pool_define_as[] = { VIRSH_COMMON_OPT_POOL_X_AS, {.name = NULL} }; static int virshBuildPoolXML(vshControl *ctl, const vshCmd *cmd, const char **retname, char **xml) { const char *name = NULL, *type = NULL, *srcHost = NULL, *srcPath = NULL, *srcDev = NULL, *srcName = NULL, *srcFormat = NULL, *target = NULL, *authType = NULL, *authUsername = NULL, *secretUsage = NULL, *adapterName = NULL, *adapterParent = NULL, *adapterWwnn = NULL, *adapterWwpn = NULL, *secretUUID = NULL, *adapterParentWwnn = NULL, *adapterParentWwpn = NULL, *adapterParentFabricWwn = NULL, *protoVer = NULL, *srcInitiator = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; VSH_EXCLUSIVE_OPTIONS("secret-usage", "secret-uuid"); if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) return false; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) return false; if (vshCommandOptStringReq(ctl, cmd, "source-host", &srcHost) < 0 || vshCommandOptStringReq(ctl, cmd, "source-path", &srcPath) < 0 || vshCommandOptStringReq(ctl, cmd, "source-dev", &srcDev) < 0 || vshCommandOptStringReq(ctl, cmd, "source-name", &srcName) < 0 || vshCommandOptStringReq(ctl, cmd, "source-format", &srcFormat) < 0 || vshCommandOptStringReq(ctl, cmd, "target", &target) < 0 || vshCommandOptStringReq(ctl, cmd, "auth-type", &authType) < 0 || vshCommandOptStringReq(ctl, cmd, "auth-username", &authUsername) < 0 || vshCommandOptStringReq(ctl, cmd, "secret-usage", &secretUsage) < 0 || vshCommandOptStringReq(ctl, cmd, "secret-uuid", &secretUUID) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-name", &adapterName) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-wwnn", &adapterWwnn) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-wwpn", &adapterWwpn) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-parent", &adapterParent) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-parent-wwnn", &adapterParentWwnn) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-parent-wwpn", &adapterParentWwpn) < 0 || vshCommandOptStringReq(ctl, cmd, "adapter-parent-fabric-wwn", &adapterParentFabricWwn) < 0 || vshCommandOptStringReq(ctl, cmd, "source-protocol-ver", &protoVer) < 0 || vshCommandOptStringReq(ctl, cmd, "source-initiator", &srcInitiator) < 0) { return false; } virBufferAsprintf(&buf, "\n", type); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "%s\n", name); if (srcHost || srcPath || srcDev || srcInitiator || srcFormat || srcName || (adapterWwnn && adapterWwpn) || adapterName) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); if (srcHost) virBufferAsprintf(&buf, "\n", srcHost); if (srcPath) virBufferAsprintf(&buf, "\n", srcPath); if (srcDev) virBufferAsprintf(&buf, "\n", srcDev); if (srcInitiator) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "\n", srcInitiator); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } if (adapterWwnn && adapterWwpn) { virBufferAddLit(&buf, "\n", adapterWwnn, adapterWwpn); } else if (adapterName) { virBufferAsprintf(&buf, "\n", adapterName); } if (authType && authUsername && (secretUsage || secretUUID)) { virBufferAsprintf(&buf, "\n", authType, authUsername); virBufferAdjustIndent(&buf, 2); if (secretUsage) virBufferAsprintf(&buf, "\n", secretUsage); else virBufferAsprintf(&buf, "\n", secretUUID); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } if (srcFormat) virBufferAsprintf(&buf, "\n", srcFormat); if (srcName) virBufferAsprintf(&buf, "%s\n", srcName); if (protoVer) virBufferAsprintf(&buf, "\n", protoVer); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } if (target) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "%s\n", target); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); *xml = virBufferContentAndReset(&buf); *retname = name; return true; } /* * "pool-create-as" command */ static const vshCmdInfo info_pool_create_as = { .help = N_("create a pool from a set of args"), .desc = N_("Create a pool."), }; static const vshCmdOptDef opts_pool_create_as[] = { VIRSH_COMMON_OPT_POOL_X_AS, VIRSH_COMMON_OPT_POOL_BUILD, VIRSH_COMMON_OPT_POOL_NO_OVERWRITE, VIRSH_COMMON_OPT_POOL_OVERWRITE, {.name = NULL} }; static bool cmdPoolCreateAs(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; const char *name; g_autofree char *xml = NULL; bool printXML = vshCommandOptBool(cmd, "print-xml"); bool build; bool overwrite; bool no_overwrite; unsigned int flags = 0; virshControl *priv = ctl->privData; build = vshCommandOptBool(cmd, "build"); overwrite = vshCommandOptBool(cmd, "overwrite"); no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, "no-overwrite", no_overwrite); if (build) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD; if (overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE; if (no_overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE; if (!virshBuildPoolXML(ctl, cmd, &name, &xml)) return false; if (printXML) { vshPrint(ctl, "%s", xml); return true; } if (!(pool = virStoragePoolCreateXML(priv->conn, xml, flags))) { vshError(ctl, _("Failed to create pool %1$s"), name); return false; } vshPrintExtra(ctl, _("Pool %1$s created\n"), name); return true; } /* * "pool-define" command */ static const vshCmdInfo info_pool_define = { .help = N_("define an inactive persistent storage pool or modify " "an existing persistent one from an XML file"), .desc = N_("Define or modify a persistent storage pool."), }; static const vshCmdOptDef opts_pool_define[] = { VIRSH_COMMON_OPT_FILE(N_("file containing an XML pool description")), {.name = "validate", .type = VSH_OT_BOOL, .help = N_("validate the XML against the schema") }, {.name = NULL} }; static bool cmdPoolDefine(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; const char *from = NULL; g_autofree char *buffer = NULL; unsigned int flags = 0; virshControl *priv = ctl->privData; if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) return false; if (vshCommandOptBool(cmd, "validate")) flags |= VIR_STORAGE_POOL_DEFINE_VALIDATE; if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) return false; if (!(pool = virStoragePoolDefineXML(priv->conn, buffer, flags))) { vshError(ctl, _("Failed to define pool from %1$s"), from); return false; } vshPrintExtra(ctl, _("Pool %1$s defined from %2$s\n"), virStoragePoolGetName(pool), from); return true; } /* * "pool-define-as" command */ static const vshCmdInfo info_pool_define_as = { .help = N_("define a pool from a set of args"), .desc = N_("Define a pool."), }; static bool cmdPoolDefineAs(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; const char *name; g_autofree char *xml = NULL; bool printXML = vshCommandOptBool(cmd, "print-xml"); virshControl *priv = ctl->privData; if (!virshBuildPoolXML(ctl, cmd, &name, &xml)) return false; if (printXML) { vshPrint(ctl, "%s", xml); return true; } if (!(pool = virStoragePoolDefineXML(priv->conn, xml, 0))) { vshError(ctl, _("Failed to define pool %1$s"), name); return false; } vshPrintExtra(ctl, _("Pool %1$s defined\n"), name); return true; } /* * "pool-build" command */ static const vshCmdInfo info_pool_build = { .help = N_("build a pool"), .desc = N_("Build a given pool."), }; static const vshCmdOptDef opts_pool_build[] = { VIRSH_COMMON_OPT_POOL_FULL(0), VIRSH_COMMON_OPT_POOL_NO_OVERWRITE, VIRSH_COMMON_OPT_POOL_OVERWRITE, {.name = NULL} }; static bool cmdPoolBuild(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name; unsigned int flags = 0; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; if (vshCommandOptBool(cmd, "no-overwrite")) flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE; if (vshCommandOptBool(cmd, "overwrite")) flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE; if (virStoragePoolBuild(pool, flags) == 0) { vshPrintExtra(ctl, _("Pool %1$s built\n"), name); } else { vshError(ctl, _("Failed to build pool %1$s"), name); ret = false; } return ret; } /* * "pool-destroy" command */ static const vshCmdInfo info_pool_destroy = { .help = N_("destroy (stop) a pool"), .desc = N_("Forcefully stop a given pool. Raw data in the pool is untouched"), }; static const vshCmdOptDef opts_pool_destroy[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE), {.name = NULL} }; static bool cmdPoolDestroy(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; if (virStoragePoolDestroy(pool) == 0) { vshPrintExtra(ctl, _("Pool %1$s destroyed\n"), name); } else { vshError(ctl, _("Failed to destroy pool %1$s"), name); ret = false; } return ret; } /* * "pool-delete" command */ static const vshCmdInfo info_pool_delete = { .help = N_("delete a pool"), .desc = N_("Delete a given pool."), }; static const vshCmdOptDef opts_pool_delete[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE), {.name = NULL} }; static bool cmdPoolDelete(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; if (virStoragePoolDelete(pool, 0) == 0) { vshPrintExtra(ctl, _("Pool %1$s deleted\n"), name); } else { vshError(ctl, _("Failed to delete pool %1$s"), name); ret = false; } return ret; } /* * "pool-refresh" command */ static const vshCmdInfo info_pool_refresh = { .help = N_("refresh a pool"), .desc = N_("Refresh a given pool."), }; static const vshCmdOptDef opts_pool_refresh[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE), {.name = NULL} }; static bool cmdPoolRefresh(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; if (virStoragePoolRefresh(pool, 0) == 0) { vshPrintExtra(ctl, _("Pool %1$s refreshed\n"), name); } else { vshError(ctl, _("Failed to refresh pool %1$s"), name); ret = false; } return ret; } /* * "pool-dumpxml" command */ static const vshCmdInfo info_pool_dumpxml = { .help = N_("pool information in XML"), .desc = N_("Output the pool information as an XML dump to stdout."), }; static const vshCmdOptDef opts_pool_dumpxml[] = { VIRSH_COMMON_OPT_POOL_FULL(0), {.name = "inactive", .type = VSH_OT_BOOL, .help = N_("show inactive defined XML") }, {.name = "xpath", .type = VSH_OT_STRING, .flags = VSH_OFLAG_REQ_OPT, .completer = virshCompleteEmpty, .help = N_("xpath expression to filter the XML document") }, {.name = "wrap", .type = VSH_OT_BOOL, .help = N_("wrap xpath results in an common root element"), }, {.name = NULL} }; static bool cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool inactive = vshCommandOptBool(cmd, "inactive"); unsigned int flags = 0; g_autofree char *xml = NULL; bool wrap = vshCommandOptBool(cmd, "wrap"); const char *xpath = NULL; if (inactive) flags |= VIR_STORAGE_XML_INACTIVE; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", NULL))) return false; if (vshCommandOptStringQuiet(ctl, cmd, "xpath", &xpath) < 0) return false; if (!(xml = virStoragePoolGetXMLDesc(pool, flags))) return false; return virshDumpXML(ctl, xml, "pool", xpath, wrap); } static int virshStoragePoolSorter(const void *a, const void *b, void *opaque G_GNUC_UNUSED) { virStoragePoolPtr *pa = (virStoragePoolPtr *) a; virStoragePoolPtr *pb = (virStoragePoolPtr *) b; if (*pa && !*pb) return -1; if (!*pa) return *pb != NULL; return vshStrcasecmp(virStoragePoolGetName(*pa), virStoragePoolGetName(*pb)); } void virshStoragePoolListFree(struct virshStoragePoolList *list) { size_t i; if (list && list->pools) { for (i = 0; i < list->npools; i++) { virshStoragePoolFree(list->pools[i]); } g_free(list->pools); } g_free(list); } struct virshStoragePoolList * virshStoragePoolListCollect(vshControl *ctl, unsigned int flags) { struct virshStoragePoolList *list = g_new0(struct virshStoragePoolList, 1); size_t i; int ret; char **names = NULL; virStoragePoolPtr pool; bool success = false; size_t deleted = 0; int persistent; int autostart; int nActivePools = 0; int nInactivePools = 0; int nAllPools = 0; virshControl *priv = ctl->privData; /* try the list with flags support (0.10.2 and later) */ if ((ret = virConnectListAllStoragePools(priv->conn, &list->pools, flags)) >= 0) { list->npools = ret; goto finished; } /* check if the command is actually supported */ if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) goto fallback; if (last_error && last_error->code == VIR_ERR_INVALID_ARG) { /* try the new API again but mask non-guaranteed flags */ unsigned int newflags = flags & (VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE | VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE); vshResetLibvirtError(); if ((ret = virConnectListAllStoragePools(priv->conn, &list->pools, newflags)) >= 0) { list->npools = ret; goto filter; } } /* there was an error during the first or second call */ vshError(ctl, "%s", _("Failed to list pools")); goto cleanup; fallback: /* fall back to old method (0.10.1 and older) */ vshResetLibvirtError(); /* There is no way to get the pool type */ if (VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE)) { vshError(ctl, "%s", _("Filtering using --type is not supported by this libvirt")); goto cleanup; } /* Get the number of active pools */ if (!VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { if ((nActivePools = virConnectNumOfStoragePools(priv->conn)) < 0) { vshError(ctl, "%s", _("Failed to get the number of active pools ")); goto cleanup; } } /* Get the number of inactive pools */ if (!VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE)) { if ((nInactivePools = virConnectNumOfDefinedStoragePools(priv->conn)) < 0) { vshError(ctl, "%s", _("Failed to get the number of inactive pools")); goto cleanup; } } nAllPools = nActivePools + nInactivePools; if (nAllPools == 0) return list; names = g_new0(char *, nAllPools); /* Retrieve a list of active storage pool names */ if (!VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { if (virConnectListStoragePools(priv->conn, names, nActivePools) < 0) { vshError(ctl, "%s", _("Failed to list active pools")); goto cleanup; } } /* Add the inactive storage pools to the end of the name list */ if (!VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { if (virConnectListDefinedStoragePools(priv->conn, &names[nActivePools], nInactivePools) < 0) { vshError(ctl, "%s", _("Failed to list inactive pools")); goto cleanup; } } list->pools = g_new0(virStoragePoolPtr, nAllPools); list->npools = 0; /* get active pools */ for (i = 0; i < nActivePools; i++) { if (!(pool = virStoragePoolLookupByName(priv->conn, names[i]))) continue; list->pools[list->npools++] = pool; } /* get inactive pools */ for (i = 0; i < nInactivePools; i++) { if (!(pool = virStoragePoolLookupByName(priv->conn, names[i]))) continue; list->pools[list->npools++] = pool; } /* truncate pools that weren't found */ deleted = nAllPools - list->npools; filter: /* filter list the list if the list was acquired by fallback means */ for (i = 0; i < list->npools; i++) { pool = list->pools[i]; /* persistence filter */ if (VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT)) { if ((persistent = virStoragePoolIsPersistent(pool)) < 0) { vshError(ctl, "%s", _("Failed to get pool persistence info")); goto cleanup; } if (!((VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT) && persistent) || (VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) && !persistent))) goto remove_entry; } /* autostart filter */ if (VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART)) { if (virStoragePoolGetAutostart(pool, &autostart) < 0) { vshError(ctl, "%s", _("Failed to get pool autostart state")); goto cleanup; } if (!((VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART) && autostart) || (VSH_MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) && !autostart))) goto remove_entry; } /* the pool matched all filters, it may stay */ continue; remove_entry: /* the pool has to be removed as it failed one of the filters */ g_clear_pointer(&list->pools[i], virshStoragePoolFree); deleted++; } finished: /* sort the list */ if (list->pools && list->npools) { g_qsort_with_data(list->pools, list->npools, sizeof(*list->pools), virshStoragePoolSorter, NULL); } /* truncate the list if filter simulation deleted entries */ if (deleted) VIR_SHRINK_N(list->pools, list->npools, deleted); success = true; cleanup: for (i = 0; i < nAllPools; i++) VIR_FREE(names[i]); if (!success) { g_clear_pointer(&list, virshStoragePoolListFree); } VIR_FREE(names); return list; } VIR_ENUM_DECL(virshStoragePoolState); VIR_ENUM_IMPL(virshStoragePoolState, VIR_STORAGE_POOL_STATE_LAST, N_("inactive"), N_("building"), N_("running"), N_("degraded"), N_("inaccessible")); static const char * virshStoragePoolStateToString(int state) { const char *str = virshStoragePoolStateTypeToString(state); return str ? _(str) : _("unknown"); } /* * "pool-list" command */ static const vshCmdInfo info_pool_list = { .help = N_("list pools"), .desc = N_("Returns list of pools."), }; static const vshCmdOptDef opts_pool_list[] = { {.name = "inactive", .type = VSH_OT_BOOL, .help = N_("list inactive pools") }, {.name = "all", .type = VSH_OT_BOOL, .help = N_("list inactive & active pools") }, {.name = "transient", .type = VSH_OT_BOOL, .help = N_("list transient pools") }, {.name = "persistent", .type = VSH_OT_BOOL, .help = N_("list persistent pools") }, {.name = "autostart", .type = VSH_OT_BOOL, .help = N_("list pools with autostart enabled") }, {.name = "no-autostart", .type = VSH_OT_BOOL, .help = N_("list pools with autostart disabled") }, {.name = "type", .type = VSH_OT_STRING, .completer = virshPoolTypeCompleter, .unwanted_positional = true, .completer_flags = VIRSH_POOL_TYPE_COMPLETER_COMMA, .help = N_("only list pool of specified type(s) (if supported)") }, {.name = "details", .type = VSH_OT_BOOL, .help = N_("display extended details for pools") }, {.name = "uuid", .type = VSH_OT_BOOL, .help = N_("list UUID of active pools only") }, {.name = "name", .type = VSH_OT_BOOL, .help = N_("list name of active pools only") }, {.name = NULL} }; static bool cmdPoolList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) { virStoragePoolInfo info; size_t i; bool ret = false; struct poolInfoText { char *state; char *autostart; char *persistent; char *capacity; char *allocation; char *available; }; struct poolInfoText *poolInfoTexts = NULL; unsigned int flags = VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE; struct virshStoragePoolList *list = NULL; const char *type = NULL; bool details = vshCommandOptBool(cmd, "details"); bool inactive, all; bool uuid = false; bool name = false; g_autoptr(vshTable) table = NULL; inactive = vshCommandOptBool(cmd, "inactive"); all = vshCommandOptBool(cmd, "all"); VSH_EXCLUSIVE_OPTIONS_VAR(all, inactive); if (inactive) flags = VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE; if (all) flags = VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE | VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE; if (vshCommandOptBool(cmd, "autostart")) flags |= VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART; if (vshCommandOptBool(cmd, "no-autostart")) flags |= VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART; if (vshCommandOptBool(cmd, "persistent")) flags |= VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT; if (vshCommandOptBool(cmd, "transient")) flags |= VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT; if (vshCommandOptBool(cmd, "uuid")) uuid = true; if (vshCommandOptBool(cmd, "name")) name = true; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) return false; VSH_EXCLUSIVE_OPTIONS("details", "uuid"); VSH_EXCLUSIVE_OPTIONS("details", "name"); if (type) { int poolType = -1; g_auto(GStrv) poolTypes = NULL; int npoolTypes = 0; if ((npoolTypes = vshStringToArray(type, &poolTypes)) < 0) return false; for (i = 0; i < npoolTypes; i++) { if ((poolType = virStoragePoolTypeFromString(poolTypes[i])) < 0) { vshError(ctl, _("Invalid pool type '%1$s'"), poolTypes[i]); return false; } switch ((virStoragePoolType) poolType) { case VIR_STORAGE_POOL_DIR: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_DIR; break; case VIR_STORAGE_POOL_FS: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_FS; break; case VIR_STORAGE_POOL_NETFS: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_NETFS; break; case VIR_STORAGE_POOL_LOGICAL: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL; break; case VIR_STORAGE_POOL_DISK: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_DISK; break; case VIR_STORAGE_POOL_ISCSI: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI; break; case VIR_STORAGE_POOL_ISCSI_DIRECT: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT; break; case VIR_STORAGE_POOL_SCSI: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_SCSI; break; case VIR_STORAGE_POOL_MPATH: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_MPATH; break; case VIR_STORAGE_POOL_RBD: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_RBD; break; case VIR_STORAGE_POOL_SHEEPDOG: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG; break; case VIR_STORAGE_POOL_GLUSTER: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER; break; case VIR_STORAGE_POOL_ZFS: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_ZFS; break; case VIR_STORAGE_POOL_VSTORAGE: flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE; break; case VIR_STORAGE_POOL_LAST: break; } } } if (!(list = virshStoragePoolListCollect(ctl, flags))) goto cleanup; poolInfoTexts = g_new0(struct poolInfoText, list->npools); /* Collect the storage pool information for display */ for (i = 0; i < list->npools; i++) { int autostart = 0, persistent = 0; /* Retrieve the autostart status of the pool */ if (virStoragePoolGetAutostart(list->pools[i], &autostart) < 0) poolInfoTexts[i].autostart = g_strdup(_("no autostart")); else poolInfoTexts[i].autostart = g_strdup(autostart ? _("yes") : _("no")); /* Retrieve the persistence status of the pool */ if (details) { persistent = virStoragePoolIsPersistent(list->pools[i]); vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n", persistent); if (persistent < 0) poolInfoTexts[i].persistent = g_strdup(_("unknown")); else poolInfoTexts[i].persistent = g_strdup(persistent ? _("yes") : _("no")); } /* Collect further extended information about the pool */ if (virStoragePoolGetInfo(list->pools[i], &info) != 0) { /* Something went wrong retrieving pool info, cope with it */ vshError(ctl, "%s", _("Could not retrieve pool information")); poolInfoTexts[i].state = g_strdup(_("unknown")); if (details) { poolInfoTexts[i].capacity = g_strdup(_("unknown")); poolInfoTexts[i].allocation = g_strdup(_("unknown")); poolInfoTexts[i].available = g_strdup(_("unknown")); } } else { /* Decide which state string to display */ if (details) { const char *state = virshStoragePoolStateToString(info.state); poolInfoTexts[i].state = g_strdup(state); /* Create the pool size related strings */ if (info.state == VIR_STORAGE_POOL_RUNNING || info.state == VIR_STORAGE_POOL_DEGRADED) { double val; const char *unit; val = vshPrettyCapacity(info.capacity, &unit); poolInfoTexts[i].capacity = g_strdup_printf("%.2lf %s", val, unit); val = vshPrettyCapacity(info.allocation, &unit); poolInfoTexts[i].allocation = g_strdup_printf("%.2lf %s", val, unit); val = vshPrettyCapacity(info.available, &unit); poolInfoTexts[i].available = g_strdup_printf("%.2lf %s", val, unit); } else { /* Capacity related information isn't available */ poolInfoTexts[i].capacity = g_strdup(_("-")); poolInfoTexts[i].allocation = g_strdup(_("-")); poolInfoTexts[i].available = g_strdup(_("-")); } } else { /* --details option was not specified, only active/inactive * state strings are used */ if (virStoragePoolIsActive(list->pools[i])) poolInfoTexts[i].state = g_strdup(_("active")); else poolInfoTexts[i].state = g_strdup(_("inactive")); } } } /* If the --details option wasn't selected, we output the pool * info using the fixed string format from previous versions to * maintain backward compatibility. */ /* Output basic info then return if --details option not selected */ if (!details) { if (uuid || name) { for (i = 0; i < list->npools; i++) { if (uuid) { char uuid_str[VIR_UUID_STRING_BUFLEN]; virStoragePoolGetUUIDString(list->pools[i], uuid_str); vshPrint(ctl, "%-36s%c", uuid_str, name ? ' ': '\n'); } if (name) { const char *name_str = virStoragePoolGetName(list->pools[i]); vshPrint(ctl, "%-20s\n", name_str); } } ret = true; goto cleanup; } /* Output old style header */ table = vshTableNew(_("Name"), _("State"), _("Autostart"), NULL); if (!table) goto cleanup; /* Output old style pool info */ for (i = 0; i < list->npools; i++) { const char *name_str = virStoragePoolGetName(list->pools[i]); if (vshTableRowAppend(table, name_str, poolInfoTexts[i].state, poolInfoTexts[i].autostart, NULL) < 0) goto cleanup; } vshTablePrintToStdout(table, ctl); /* Cleanup and return */ ret = true; goto cleanup; } /* We only get here if the --details option was selected. */ /* Insert the header into table */ table = vshTableNew(_("Name"), _("State"), _("Autostart"), _("Persistent"), _("Capacity"), _("Allocation"), _("Available"), NULL); if (!table) goto cleanup; /* Insert the pool info rows into table */ for (i = 0; i < list->npools; i++) { if (vshTableRowAppend(table, virStoragePoolGetName(list->pools[i]), poolInfoTexts[i].state, poolInfoTexts[i].autostart, poolInfoTexts[i].persistent, poolInfoTexts[i].capacity, poolInfoTexts[i].allocation, poolInfoTexts[i].available, NULL) < 0) goto cleanup; } vshTablePrintToStdout(table, ctl); /* Cleanup and return */ ret = true; cleanup: if (list && list->npools) { for (i = 0; i < list->npools; i++) { VIR_FREE(poolInfoTexts[i].state); VIR_FREE(poolInfoTexts[i].autostart); VIR_FREE(poolInfoTexts[i].persistent); VIR_FREE(poolInfoTexts[i].capacity); VIR_FREE(poolInfoTexts[i].allocation); VIR_FREE(poolInfoTexts[i].available); } } VIR_FREE(poolInfoTexts); virshStoragePoolListFree(list); return ret; } /* * "find-storage-pool-sources-as" command */ static const vshCmdInfo info_find_storage_pool_sources_as = { .help = N_("find potential storage pool sources"), .desc = N_("Returns XML document."), }; static const vshCmdOptDef opts_find_storage_pool_sources_as[] = { {.name = "type", .type = VSH_OT_STRING, .positional = true, .required = true, .completer = virshPoolTypeCompleter, .help = N_("type of storage pool sources to find") }, {.name = "host", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshCompleteEmpty, .help = N_("optional host to query") }, {.name = "port", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshCompleteEmpty, .help = N_("optional port to query") }, {.name = "initiator", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshCompleteEmpty, .help = N_("optional initiator IQN to use for query") }, {.name = NULL} }; static bool cmdPoolDiscoverSourcesAs(vshControl * ctl, const vshCmd * cmd G_GNUC_UNUSED) { const char *type = NULL, *host = NULL; g_autofree char *srcSpec = NULL; g_autofree char *srcList = NULL; const char *initiator = NULL; virshControl *priv = ctl->privData; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0 || vshCommandOptStringReq(ctl, cmd, "host", &host) < 0 || vshCommandOptStringReq(ctl, cmd, "initiator", &initiator) < 0) return false; if (host) { const char *port = NULL; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; if (vshCommandOptStringReq(ctl, cmd, "port", &port) < 0) { vshError(ctl, "%s", _("missing argument")); return false; } virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "\n"); if (initiator) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "\n", initiator); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); srcSpec = virBufferContentAndReset(&buf); } srcList = virConnectFindStoragePoolSources(priv->conn, type, srcSpec, 0); if (srcList == NULL) { vshError(ctl, _("Failed to find any %1$s pool sources"), type); return false; } vshPrint(ctl, "%s", srcList); return true; } /* * "find-storage-pool-sources" command */ static const vshCmdInfo info_find_storage_pool_sources = { .help = N_("discover potential storage pool sources"), .desc = N_("Returns XML document."), }; static const vshCmdOptDef opts_find_storage_pool_sources[] = { {.name = "type", .type = VSH_OT_STRING, .positional = true, .required = true, .completer = virshPoolTypeCompleter, .help = N_("type of storage pool sources to discover") }, {.name = "srcSpec", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshCompletePathLocalExisting, .help = N_("optional file of source xml to query for pools") }, {.name = NULL} }; static bool cmdPoolDiscoverSources(vshControl * ctl, const vshCmd * cmd G_GNUC_UNUSED) { const char *type = NULL, *srcSpecFile = NULL; char *srcSpec = NULL, *srcList; virshControl *priv = ctl->privData; if (vshCommandOptStringReq(ctl, cmd, "type", &type) < 0) return false; if (vshCommandOptStringReq(ctl, cmd, "srcSpec", &srcSpecFile) < 0) return false; if (srcSpecFile && virFileReadAll(srcSpecFile, VSH_MAX_XML_FILE, &srcSpec) < 0) return false; srcList = virConnectFindStoragePoolSources(priv->conn, type, srcSpec, 0); VIR_FREE(srcSpec); if (srcList == NULL) { vshError(ctl, _("Failed to find any %1$s pool sources"), type); return false; } vshPrint(ctl, "%s", srcList); VIR_FREE(srcList); return true; } /* * "pool-info" command */ static const vshCmdInfo info_pool_info = { .help = N_("storage pool information"), .desc = N_("Returns basic information about the storage pool."), }; static const vshCmdOptDef opts_pool_info[] = { VIRSH_COMMON_OPT_POOL_FULL(0), {.name = "bytes", .type = VSH_OT_BOOL, .help = N_("Return pool info in bytes"), }, {.name = NULL} }; static bool cmdPoolInfo(vshControl *ctl, const vshCmd *cmd) { virStoragePoolInfo info; g_autoptr(virshStoragePool) pool = NULL; int autostart = 0; bool ret = true; bool bytes = false; char uuid[VIR_UUID_STRING_BUFLEN]; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", NULL))) return false; bytes = vshCommandOptBool(cmd, "bytes"); vshPrint(ctl, "%-15s %s\n", _("Name:"), virStoragePoolGetName(pool)); if (virStoragePoolGetUUIDString(pool, &uuid[0]) == 0) vshPrint(ctl, "%-15s %s\n", _("UUID:"), uuid); if (virStoragePoolGetInfo(pool, &info) == 0) { double val; const char *unit; int persistent; vshPrint(ctl, "%-15s %s\n", _("State:"), virshStoragePoolStateToString(info.state)); /* Check and display whether the pool is persistent or not */ persistent = virStoragePoolIsPersistent(pool); vshDebug(ctl, VSH_ERR_DEBUG, "Pool persistent flag value: %d\n", persistent); if (persistent < 0) vshPrint(ctl, "%-15s %s\n", _("Persistent:"), _("unknown")); else vshPrint(ctl, "%-15s %s\n", _("Persistent:"), persistent ? _("yes") : _("no")); /* Check and display whether the pool is autostarted or not */ if (virStoragePoolGetAutostart(pool, &autostart) < 0) vshPrint(ctl, "%-15s %s\n", _("Autostart:"), _("no autostart")); else vshPrint(ctl, "%-15s %s\n", _("Autostart:"), autostart ? _("yes") : _("no")); if (info.state == VIR_STORAGE_POOL_RUNNING || info.state == VIR_STORAGE_POOL_DEGRADED) { if (bytes) { vshPrint(ctl, "%-15s %llu\n", _("Capacity:"), info.capacity); vshPrint(ctl, "%-15s %llu\n", _("Allocation:"), info.allocation); vshPrint(ctl, "%-15s %llu\n", _("Available:"), info.available); } else { val = vshPrettyCapacity(info.capacity, &unit); vshPrint(ctl, "%-15s %2.2lf %s\n", _("Capacity:"), val, unit); val = vshPrettyCapacity(info.allocation, &unit); vshPrint(ctl, "%-15s %2.2lf %s\n", _("Allocation:"), val, unit); val = vshPrettyCapacity(info.available, &unit); vshPrint(ctl, "%-15s %2.2lf %s\n", _("Available:"), val, unit); } } } else { ret = false; } return ret; } /* * "pool-name" command */ static const vshCmdInfo info_pool_name = { .help = N_("convert a pool UUID to pool name"), .desc = "", }; static const vshCmdOptDef opts_pool_name[] = { VIRSH_COMMON_OPT_POOL_FULL(0), {.name = NULL} }; static bool cmdPoolName(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; if (!(pool = virshCommandOptPoolBy(ctl, cmd, "pool", NULL, VIRSH_BYUUID))) return false; vshPrint(ctl, "%s\n", virStoragePoolGetName(pool)); return true; } /* * "pool-start" command */ static const vshCmdInfo info_pool_start = { .help = N_("start a (previously defined) inactive pool"), .desc = N_("Start a pool."), }; static const vshCmdOptDef opts_pool_start[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE), VIRSH_COMMON_OPT_POOL_BUILD, VIRSH_COMMON_OPT_POOL_NO_OVERWRITE, VIRSH_COMMON_OPT_POOL_OVERWRITE, {.name = NULL} }; static bool cmdPoolStart(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name = NULL; bool build; bool overwrite; bool no_overwrite; unsigned int flags = 0; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; build = vshCommandOptBool(cmd, "build"); overwrite = vshCommandOptBool(cmd, "overwrite"); no_overwrite = vshCommandOptBool(cmd, "no-overwrite"); VSH_EXCLUSIVE_OPTIONS_EXPR("overwrite", overwrite, "no-overwrite", no_overwrite); if (build) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD; if (overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE; if (no_overwrite) flags |= VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE; if (virStoragePoolCreate(pool, flags) == 0) { vshPrintExtra(ctl, _("Pool %1$s started\n"), name); } else { vshError(ctl, _("Failed to start pool %1$s"), name); ret = false; } return ret; } /* * "pool-undefine" command */ static const vshCmdInfo info_pool_undefine = { .help = N_("undefine an inactive pool"), .desc = N_("Undefine the configuration for an inactive pool."), }; static const vshCmdOptDef opts_pool_undefine[] = { VIRSH_COMMON_OPT_POOL_FULL(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT), {.name = NULL} }; static bool cmdPoolUndefine(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = true; const char *name; if (!(pool = virshCommandOptPool(ctl, cmd, "pool", &name))) return false; if (virStoragePoolUndefine(pool) == 0) { vshPrintExtra(ctl, _("Pool %1$s has been undefined\n"), name); } else { vshError(ctl, _("Failed to undefine pool %1$s"), name); ret = false; } return ret; } /* * "pool-uuid" command */ static const vshCmdInfo info_pool_uuid = { .help = N_("convert a pool name to pool UUID"), .desc = "", }; static const vshCmdOptDef opts_pool_uuid[] = { VIRSH_COMMON_OPT_POOL_FULL(0), {.name = NULL} }; static bool cmdPoolUuid(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; char uuid[VIR_UUID_STRING_BUFLEN]; if (!(pool = virshCommandOptPoolBy(ctl, cmd, "pool", NULL, VIRSH_BYNAME))) return false; if (virStoragePoolGetUUIDString(pool, uuid) != -1) vshPrint(ctl, "%s\n", uuid); else vshError(ctl, "%s", _("failed to get pool UUID")); return true; } /* * "pool-edit" command */ static const vshCmdInfo info_pool_edit = { .help = N_("edit XML configuration for a storage pool"), .desc = N_("Edit the XML configuration for a storage pool."), }; static const vshCmdOptDef opts_pool_edit[] = { VIRSH_COMMON_OPT_POOL_FULL(0), {.name = NULL} }; static bool cmdPoolEdit(vshControl *ctl, const vshCmd *cmd) { bool ret = false; g_autoptr(virshStoragePool) pool = NULL; g_autoptr(virshStoragePool) pool_edited = NULL; unsigned int flags = VIR_STORAGE_XML_INACTIVE; g_autofree char *tmp_desc = NULL; virshControl *priv = ctl->privData; pool = virshCommandOptPool(ctl, cmd, "pool", NULL); if (pool == NULL) goto cleanup; /* Some old daemons don't support _INACTIVE flag */ if (!(tmp_desc = virStoragePoolGetXMLDesc(pool, flags))) { if (last_error->code == VIR_ERR_INVALID_ARG) { flags &= ~VIR_STORAGE_XML_INACTIVE; vshResetLibvirtError(); } else { goto cleanup; } } #define EDIT_GET_XML virStoragePoolGetXMLDesc(pool, flags) #define EDIT_NOT_CHANGED \ do { \ vshPrintExtra(ctl, _("Pool %1$s XML configuration not changed.\n"), \ virStoragePoolGetName(pool)); \ ret = true; \ goto edit_cleanup; \ } while (0) #define EDIT_DEFINE \ (pool_edited = virStoragePoolDefineXML(priv->conn, doc_edited, 0)) #include "virsh-edit.c" vshPrintExtra(ctl, _("Pool %1$s XML configuration edited.\n"), virStoragePoolGetName(pool_edited)); ret = true; cleanup: return ret; } /* * "pool-event" command */ VIR_ENUM_DECL(virshPoolEvent); VIR_ENUM_IMPL(virshPoolEvent, VIR_STORAGE_POOL_EVENT_LAST, N_("Defined"), N_("Undefined"), N_("Started"), N_("Stopped"), N_("Created"), N_("Deleted")); static const char * virshPoolEventToString(int event) { const char *str = virshPoolEventTypeToString(event); return str ? _(str) : _("unknown"); } struct virshPoolEventData { vshControl *ctl; bool loop; bool timestamp; int count; virshPoolEventCallback *cb; }; typedef struct virshPoolEventData virshPoolEventData; static void vshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED, virStoragePoolPtr pool, int event, int detail G_GNUC_UNUSED, void *opaque) { virshPoolEventData *data = opaque; if (!data->loop && data->count) return; if (data->timestamp) { char timestamp[VIR_TIME_STRING_BUFLEN]; if (virTimeStringNowRaw(timestamp) < 0) timestamp[0] = '\0'; vshPrint(data->ctl, _("%1$s: event 'lifecycle' for storage pool %2$s: %3$s\n"), timestamp, virStoragePoolGetName(pool), virshPoolEventToString(event)); } else { vshPrint(data->ctl, _("event 'lifecycle' for storage pool %1$s: %2$s\n"), virStoragePoolGetName(pool), virshPoolEventToString(event)); } data->count++; if (!data->loop) vshEventDone(data->ctl); } static void vshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED, virStoragePoolPtr pool, void *opaque) { virshPoolEventData *data = opaque; if (!data->loop && data->count) return; if (data->timestamp) { char timestamp[VIR_TIME_STRING_BUFLEN]; if (virTimeStringNowRaw(timestamp) < 0) timestamp[0] = '\0'; vshPrint(data->ctl, _("%1$s: event '%2$s' for storage pool %3$s\n"), timestamp, data->cb->name, virStoragePoolGetName(pool)); } else { vshPrint(data->ctl, _("event '%1$s' for storage pool %2$s\n"), data->cb->name, virStoragePoolGetName(pool)); } data->count++; if (!data->loop) vshEventDone(data->ctl); } virshPoolEventCallback virshPoolEventCallbacks[] = { { "lifecycle", VIR_STORAGE_POOL_EVENT_CALLBACK(vshEventLifecyclePrint), }, { "refresh", vshEventGenericPrint, } }; G_STATIC_ASSERT(VIR_STORAGE_POOL_EVENT_ID_LAST == G_N_ELEMENTS(virshPoolEventCallbacks)); static const vshCmdInfo info_pool_event = { .help = N_("Storage Pool Events"), .desc = N_("List event types, or wait for storage pool events to occur"), }; static const vshCmdOptDef opts_pool_event[] = { {.name = "pool", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshStoragePoolNameCompleter, .help = N_("filter by storage pool name or uuid") }, {.name = "event", .type = VSH_OT_STRING, .unwanted_positional = true, .completer = virshPoolEventNameCompleter, .help = N_("which event type to wait for") }, {.name = "loop", .type = VSH_OT_BOOL, .help = N_("loop until timeout or interrupt, rather than one-shot") }, {.name = "timeout", .type = VSH_OT_INT, .unwanted_positional = true, .help = N_("timeout seconds") }, {.name = "list", .type = VSH_OT_BOOL, .help = N_("list valid event types") }, {.name = "timestamp", .type = VSH_OT_BOOL, .help = N_("show timestamp for each printed event") }, {.name = NULL} }; static bool cmdPoolEvent(vshControl *ctl, const vshCmd *cmd) { g_autoptr(virshStoragePool) pool = NULL; bool ret = false; int eventId = -1; int timeout = 0; virshPoolEventData data; const char *eventName = NULL; int event; virshControl *priv = ctl->privData; if (vshCommandOptBool(cmd, "list")) { size_t i; for (i = 0; i < VIR_STORAGE_POOL_EVENT_ID_LAST; i++) vshPrint(ctl, "%s\n", virshPoolEventCallbacks[i].name); return true; } if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0) return false; if (!eventName) { vshError(ctl, "%s", _("either --list or --event is required")); return false; } for (event = 0; event < VIR_STORAGE_POOL_EVENT_ID_LAST; event++) if (STREQ(eventName, virshPoolEventCallbacks[event].name)) break; if (event == VIR_STORAGE_POOL_EVENT_ID_LAST) { vshError(ctl, _("unknown event type %1$s"), eventName); return false; } data.ctl = ctl; data.loop = vshCommandOptBool(cmd, "loop"); data.timestamp = vshCommandOptBool(cmd, "timestamp"); data.count = 0; data.cb = &virshPoolEventCallbacks[event]; if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) return false; if (vshCommandOptBool(cmd, "pool")) pool = virshCommandOptPool(ctl, cmd, "pool", NULL); if (vshEventStart(ctl, timeout) < 0) goto cleanup; if ((eventId = virConnectStoragePoolEventRegisterAny(priv->conn, pool, event, data.cb->cb, &data, NULL)) < 0) goto cleanup; switch (vshEventWait(ctl)) { case VSH_EVENT_INTERRUPT: vshPrint(ctl, "%s", _("event loop interrupted\n")); break; case VSH_EVENT_TIMEOUT: vshPrint(ctl, "%s", _("event loop timed out\n")); break; case VSH_EVENT_DONE: break; default: goto cleanup; } vshPrint(ctl, _("events received: %1$d\n"), data.count); if (data.count) ret = true; cleanup: vshEventCleanup(ctl); if (eventId >= 0 && virConnectStoragePoolEventDeregisterAny(priv->conn, eventId) < 0) ret = false; return ret; } /* * "pool-capabilities" command */ static const vshCmdInfo info_pool_capabilities = { .help = N_("storage pool capabilities"), .desc = N_("Returns capabilities of storage pool support."), }; static const vshCmdOptDef opts_pool_capabilities[] = { {.name = NULL} }; static bool cmdPoolCapabilities(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) { const unsigned int flags = 0; /* No flags so far */ virshControl *priv = ctl->privData; g_autofree char *caps = NULL; caps = virConnectGetStoragePoolCapabilities(priv->conn, flags); if (!caps) { vshError(ctl, "%s", _("failed to get storage pool capabilities")); return false; } vshPrint(ctl, "%s\n", caps); return true; } const vshCmdDef storagePoolCmds[] = { {.name = "find-storage-pool-sources-as", .handler = cmdPoolDiscoverSourcesAs, .opts = opts_find_storage_pool_sources_as, .info = &info_find_storage_pool_sources_as, .flags = 0 }, {.name = "find-storage-pool-sources", .handler = cmdPoolDiscoverSources, .opts = opts_find_storage_pool_sources, .info = &info_find_storage_pool_sources, .flags = 0 }, {.name = "pool-autostart", .handler = cmdPoolAutostart, .opts = opts_pool_autostart, .info = &info_pool_autostart, .flags = 0 }, {.name = "pool-build", .handler = cmdPoolBuild, .opts = opts_pool_build, .info = &info_pool_build, .flags = 0 }, {.name = "pool-create-as", .handler = cmdPoolCreateAs, .opts = opts_pool_create_as, .info = &info_pool_create_as, .flags = 0 }, {.name = "pool-create", .handler = cmdPoolCreate, .opts = opts_pool_create, .info = &info_pool_create, .flags = 0 }, {.name = "pool-define-as", .handler = cmdPoolDefineAs, .opts = opts_pool_define_as, .info = &info_pool_define_as, .flags = 0 }, {.name = "pool-define", .handler = cmdPoolDefine, .opts = opts_pool_define, .info = &info_pool_define, .flags = 0 }, {.name = "pool-delete", .handler = cmdPoolDelete, .opts = opts_pool_delete, .info = &info_pool_delete, .flags = 0 }, {.name = "pool-destroy", .handler = cmdPoolDestroy, .opts = opts_pool_destroy, .info = &info_pool_destroy, .flags = 0 }, {.name = "pool-dumpxml", .handler = cmdPoolDumpXML, .opts = opts_pool_dumpxml, .info = &info_pool_dumpxml, .flags = 0 }, {.name = "pool-edit", .handler = cmdPoolEdit, .opts = opts_pool_edit, .info = &info_pool_edit, .flags = 0 }, {.name = "pool-info", .handler = cmdPoolInfo, .opts = opts_pool_info, .info = &info_pool_info, .flags = 0 }, {.name = "pool-list", .handler = cmdPoolList, .opts = opts_pool_list, .info = &info_pool_list, .flags = 0 }, {.name = "pool-name", .handler = cmdPoolName, .opts = opts_pool_name, .info = &info_pool_name, .flags = 0 }, {.name = "pool-refresh", .handler = cmdPoolRefresh, .opts = opts_pool_refresh, .info = &info_pool_refresh, .flags = 0 }, {.name = "pool-start", .handler = cmdPoolStart, .opts = opts_pool_start, .info = &info_pool_start, .flags = 0 }, {.name = "pool-undefine", .handler = cmdPoolUndefine, .opts = opts_pool_undefine, .info = &info_pool_undefine, .flags = 0 }, {.name = "pool-uuid", .handler = cmdPoolUuid, .opts = opts_pool_uuid, .info = &info_pool_uuid, .flags = 0 }, {.name = "pool-event", .handler = cmdPoolEvent, .opts = opts_pool_event, .info = &info_pool_event, .flags = 0 }, {.name = "pool-capabilities", .handler = cmdPoolCapabilities, .opts = opts_pool_capabilities, .info = &info_pool_capabilities, .flags = 0 }, {.name = NULL} };