mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
a9d8bf269a
Allowing an 'all' and 'unmanaged' value is more intuitive, and provides a simple way for users to get back to original LVM behavior of metadata written to all PVs in the volume group. If the user requests "--vgmetadatacopies unmanaged", this instructs LVM not to manage the ignore bits to achieve a specific number of metadata copies in the volume group. The user is free to use "pvchange --metadataignore" to control the mdas on a per-PV basis. If the user requests "--vgmetadatacopies all", this instructs LVM to do 2 things: 1) clear all ignore bits, and 2) set the "unmanaged" policy going forward. Internally, we use the special MAX_UINT32 value to indicate 'all'. This 'just' works since it's the largest value possible for the field and so all 'ignore' bits on all mdas in the VG will get cleared inside _vg_metadata_balance(). However, after we've called the _vg_metadata_balance function, we check for the special 'all' value, and if set, we write the "unmanaged" value into the metadata. As such, the 'all' value is never written to disk. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
1302 lines
33 KiB
C
1302 lines
33 KiB
C
/*
|
|
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "lib.h"
|
|
#include "metadata.h"
|
|
#include "report.h"
|
|
#include "toolcontext.h"
|
|
#include "lvm-string.h"
|
|
#include "display.h"
|
|
#include "activate.h"
|
|
#include "segtype.h"
|
|
#include "str_list.h"
|
|
#include "lvmcache.h"
|
|
|
|
#include <stddef.h> /* offsetof() */
|
|
|
|
struct lvm_report_object {
|
|
struct volume_group *vg;
|
|
struct logical_volume *lv;
|
|
struct physical_volume *pv;
|
|
struct lv_segment *seg;
|
|
struct pv_segment *pvseg;
|
|
};
|
|
|
|
static char _alloc_policy_char(alloc_policy_t alloc)
|
|
{
|
|
switch (alloc) {
|
|
case ALLOC_CONTIGUOUS:
|
|
return 'c';
|
|
case ALLOC_CLING:
|
|
return 'l';
|
|
case ALLOC_NORMAL:
|
|
return 'n';
|
|
case ALLOC_ANYWHERE:
|
|
return 'a';
|
|
default:
|
|
return 'i';
|
|
}
|
|
}
|
|
|
|
static const uint64_t _minusone64 = UINT64_C(-1);
|
|
static const int32_t _minusone32 = INT32_C(-1);
|
|
|
|
/*
|
|
* Data-munging functions to prepare each data type for display and sorting
|
|
*/
|
|
static int _string_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
return dm_report_field_string(rh, field, (const char **) data);
|
|
}
|
|
|
|
static int _dev_name_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const char *name = dev_name(*(const struct device * const *) data);
|
|
|
|
return dm_report_field_string(rh, field, &name);
|
|
}
|
|
|
|
static int _format_pvsegs(struct dm_pool *mem, struct dm_report_field *field,
|
|
const void *data, int range_format)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
unsigned int s;
|
|
const char *name = NULL;
|
|
uint32_t extent = 0;
|
|
char extent_str[32];
|
|
|
|
if (!dm_pool_begin_object(mem, 256)) {
|
|
log_error("dm_pool_begin_object failed");
|
|
return 0;
|
|
}
|
|
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
switch (seg_type(seg, s)) {
|
|
case AREA_LV:
|
|
name = seg_lv(seg, s)->name;
|
|
extent = seg_le(seg, s);
|
|
break;
|
|
case AREA_PV:
|
|
name = dev_name(seg_dev(seg, s));
|
|
extent = seg_pe(seg, s);
|
|
break;
|
|
case AREA_UNASSIGNED:
|
|
name = "unassigned";
|
|
extent = 0;
|
|
}
|
|
|
|
if (!dm_pool_grow_object(mem, name, strlen(name))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(extent_str, sizeof(extent_str),
|
|
"%s%" PRIu32 "%s",
|
|
range_format ? ":" : "(", extent,
|
|
range_format ? "-" : ")") < 0) {
|
|
log_error("Extent number dm_snprintf failed");
|
|
return 0;
|
|
}
|
|
if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
if (range_format) {
|
|
if (dm_snprintf(extent_str, sizeof(extent_str),
|
|
"%" PRIu32, extent + seg->area_len - 1) < 0) {
|
|
log_error("Extent number dm_snprintf failed");
|
|
return 0;
|
|
}
|
|
if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((s != seg->area_count - 1) &&
|
|
!dm_pool_grow_object(mem, range_format ? " " : ",", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dm_pool_grow_object(mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
dm_report_field_set_value(field, dm_pool_end_object(mem), NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _devices_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
return _format_pvsegs(mem, field, data, 0);
|
|
}
|
|
|
|
static int _peranges_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
return _format_pvsegs(mem, field, data, 1);
|
|
}
|
|
|
|
static int _tags_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct dm_list *tags = (const struct dm_list *) data;
|
|
struct str_list *sl;
|
|
|
|
if (!dm_pool_begin_object(mem, 256)) {
|
|
log_error("dm_pool_begin_object failed");
|
|
return 0;
|
|
}
|
|
|
|
dm_list_iterate_items(sl, tags) {
|
|
if (!dm_pool_grow_object(mem, sl->str, strlen(sl->str)) ||
|
|
(sl->list.n != tags && !dm_pool_grow_object(mem, ",", 1))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dm_pool_grow_object(mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
dm_report_field_set_value(field, dm_pool_end_object(mem), NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _modules_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct dm_list *modules;
|
|
|
|
if (!(modules = str_list_create(mem))) {
|
|
log_error("modules str_list allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!list_lv_modules(mem, lv, modules))
|
|
return_0;
|
|
|
|
return _tags_disp(rh, mem, field, modules, private);
|
|
}
|
|
|
|
static int _vgfmt_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
|
|
if (!vg->fid) {
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
return _string_disp(rh, mem, field, &vg->fid->fmt->name, private);
|
|
}
|
|
|
|
static int _pvfmt_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
|
|
if (!pv->fmt) {
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
return _string_disp(rh, mem, field, &pv->fmt->name, private);
|
|
}
|
|
|
|
static int _lvkmaj_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 0, 0) && info.exists)
|
|
return dm_report_field_int(rh, field, &info.major);
|
|
|
|
return dm_report_field_int32(rh, field, &_minusone32);
|
|
}
|
|
|
|
static int _lvkmin_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 0, 0) && info.exists)
|
|
return dm_report_field_int(rh, field, &info.minor);
|
|
|
|
return dm_report_field_int32(rh, field, &_minusone32);
|
|
}
|
|
|
|
static int _lv_mimage_in_sync(const struct logical_volume *lv)
|
|
{
|
|
float percent;
|
|
percent_range_t percent_range;
|
|
struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv));
|
|
|
|
if (!(lv->status & MIRROR_IMAGE) || !mirror_seg)
|
|
return_0;
|
|
|
|
if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent,
|
|
&percent_range, NULL))
|
|
return_0;
|
|
|
|
return (percent_range == PERCENT_100) ? 1 : 0;
|
|
}
|
|
|
|
static int _lvstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
char *repstr;
|
|
float snap_percent;
|
|
percent_range_t percent_range;
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, 7))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
/* Blank if this is a "free space" LV. */
|
|
if (!*lv->name)
|
|
goto out;
|
|
|
|
if (lv->status & PVMOVE)
|
|
repstr[0] = 'p';
|
|
else if (lv->status & CONVERTING)
|
|
repstr[0] = 'c';
|
|
else if (lv->status & VIRTUAL)
|
|
repstr[0] = 'v';
|
|
/* Origin takes precedence over Mirror */
|
|
else if (lv_is_origin(lv)) {
|
|
if (lv_is_merging_origin(lv))
|
|
repstr[0] = 'O';
|
|
else
|
|
repstr[0] = 'o';
|
|
}
|
|
else if (lv->status & MIRRORED) {
|
|
if (lv->status & MIRROR_NOTSYNCED)
|
|
repstr[0] = 'M';
|
|
else
|
|
repstr[0] = 'm';
|
|
}else if (lv->status & MIRROR_IMAGE)
|
|
if (_lv_mimage_in_sync(lv))
|
|
repstr[0] = 'i';
|
|
else
|
|
repstr[0] = 'I';
|
|
else if (lv->status & MIRROR_LOG)
|
|
repstr[0] = 'l';
|
|
else if (lv_is_cow(lv)) {
|
|
if (lv_is_merging_cow(lv))
|
|
repstr[0] = 'S';
|
|
else
|
|
repstr[0] = 's';
|
|
} else
|
|
repstr[0] = '-';
|
|
|
|
if (lv->status & PVMOVE)
|
|
repstr[1] = '-';
|
|
else if (lv->status & LVM_WRITE)
|
|
repstr[1] = 'w';
|
|
else if (lv->status & LVM_READ)
|
|
repstr[1] = 'r';
|
|
else
|
|
repstr[1] = '-';
|
|
|
|
repstr[2] = _alloc_policy_char(lv->alloc);
|
|
|
|
if (lv->status & LOCKED)
|
|
repstr[2] = toupper(repstr[2]);
|
|
|
|
if (lv->status & FIXED_MINOR)
|
|
repstr[3] = 'm'; /* Fixed Minor */
|
|
else
|
|
repstr[3] = '-';
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 1, 0) && info.exists) {
|
|
if (info.suspended)
|
|
repstr[4] = 's'; /* Suspended */
|
|
else if (info.live_table)
|
|
repstr[4] = 'a'; /* Active */
|
|
else if (info.inactive_table)
|
|
repstr[4] = 'i'; /* Inactive with table */
|
|
else
|
|
repstr[4] = 'd'; /* Inactive without table */
|
|
|
|
/* Snapshot dropped? */
|
|
if (info.live_table && lv_is_cow(lv) &&
|
|
(!lv_snapshot_percent(lv, &snap_percent, &percent_range) ||
|
|
percent_range == PERCENT_INVALID)) {
|
|
repstr[0] = toupper(repstr[0]);
|
|
if (info.suspended)
|
|
repstr[4] = 'S'; /* Susp Inv snapshot */
|
|
else
|
|
repstr[4] = 'I'; /* Invalid snapshot */
|
|
}
|
|
|
|
if (info.open_count)
|
|
repstr[5] = 'o'; /* Open */
|
|
else
|
|
repstr[5] = '-';
|
|
} else {
|
|
repstr[4] = '-';
|
|
repstr[5] = '-';
|
|
}
|
|
|
|
out:
|
|
dm_report_field_set_value(field, repstr, NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _pvstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const uint32_t status = *(const uint32_t *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, 3))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (status & ALLOCATABLE_PV)
|
|
repstr[0] = 'a';
|
|
else
|
|
repstr[0] = '-';
|
|
|
|
if (status & EXPORTED_VG)
|
|
repstr[1] = 'x';
|
|
else
|
|
repstr[1] = '-';
|
|
|
|
dm_report_field_set_value(field, repstr, NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _vgstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, 7))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (vg->status & LVM_WRITE)
|
|
repstr[0] = 'w';
|
|
else
|
|
repstr[0] = 'r';
|
|
|
|
if (vg_is_resizeable(vg))
|
|
repstr[1] = 'z';
|
|
else
|
|
repstr[1] = '-';
|
|
|
|
if (vg_is_exported(vg))
|
|
repstr[2] = 'x';
|
|
else
|
|
repstr[2] = '-';
|
|
|
|
if (vg_missing_pv_count(vg))
|
|
repstr[3] = 'p';
|
|
else
|
|
repstr[3] = '-';
|
|
|
|
repstr[4] = _alloc_policy_char(vg->alloc);
|
|
|
|
if (vg_is_clustered(vg))
|
|
repstr[5] = 'c';
|
|
else
|
|
repstr[5] = '-';
|
|
|
|
dm_report_field_set_value(field, repstr, NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _segtype_disp(struct dm_report *rh __attribute((unused)),
|
|
struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
|
|
if (seg->area_count == 1) {
|
|
dm_report_field_set_value(field, "linear", NULL);
|
|
return 1;
|
|
}
|
|
|
|
dm_report_field_set_value(field, seg->segtype->ops->name(seg), NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lv_segment *seg;
|
|
|
|
dm_list_iterate_items(seg, &lv->segments) {
|
|
if (!seg_is_mirrored(seg) || !seg->log_lv)
|
|
continue;
|
|
return dm_report_field_string(rh, field,
|
|
(const char **) &seg->log_lv->name);
|
|
}
|
|
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr, *lvname;
|
|
size_t len;
|
|
|
|
if (lv_is_visible(lv)) {
|
|
repstr = lv->name;
|
|
return dm_report_field_string(rh, field, (const char **) &repstr);
|
|
}
|
|
|
|
len = strlen(lv->name) + 3;
|
|
if (!(repstr = dm_pool_zalloc(mem, len))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, len, "[%s]", lv->name) < 0) {
|
|
log_error("lvname snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(lvname = dm_pool_strdup(mem, lv->name))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
dm_report_field_set_value(field, repstr, lvname);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvpath_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr;
|
|
size_t len;
|
|
|
|
len = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) + strlen(lv->name) + 2;
|
|
if (!(repstr = dm_pool_zalloc(mem, len))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, len, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) {
|
|
log_error("lvpath snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
dm_report_field_set_value(field, repstr, NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv_is_cow(lv))
|
|
return _lvname_disp(rh, mem, field, origin_from_cow(lv), private);
|
|
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const char *name;
|
|
struct lv_segment *seg;
|
|
|
|
dm_list_iterate_items(seg, &lv->segments) {
|
|
if (!(seg->status & PVMOVE))
|
|
continue;
|
|
name = dev_name(seg_dev(seg, 0));
|
|
return dm_report_field_string(rh, field, &name);
|
|
}
|
|
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _convertlv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const char *name = NULL;
|
|
struct lv_segment *seg;
|
|
|
|
if (lv->status & CONVERTING) {
|
|
if (lv->status & MIRRORED) {
|
|
seg = first_seg(lv);
|
|
|
|
/* Temporary mirror is always area_num == 0 */
|
|
if (seg_type(seg, 0) == AREA_LV &&
|
|
is_temporary_mirror_layer(seg_lv(seg, 0)))
|
|
name = seg_lv(seg, 0)->name;
|
|
}
|
|
}
|
|
|
|
if (name)
|
|
return dm_report_field_string(rh, field, &name);
|
|
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _size32_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const uint32_t size = *(const uint32_t *) data;
|
|
const char *disp, *repstr;
|
|
uint64_t *sortval;
|
|
|
|
if (!*(disp = display_size_units(private, (uint64_t) size)))
|
|
return_0;
|
|
|
|
if (!(repstr = dm_pool_strdup(mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (const uint64_t) size;
|
|
|
|
dm_report_field_set_value(field, repstr, sortval);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _size64_disp(struct dm_report *rh __attribute((unused)),
|
|
struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const uint64_t size = *(const uint64_t *) data;
|
|
const char *disp, *repstr;
|
|
uint64_t *sortval;
|
|
|
|
if (!*(disp = display_size_units(private, size)))
|
|
return_0;
|
|
|
|
if (!(repstr = dm_pool_strdup(mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = size;
|
|
dm_report_field_set_value(field, repstr, sortval);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv->read_ahead == DM_READ_AHEAD_AUTO) {
|
|
dm_report_field_set_value(field, "auto", &_minusone64);
|
|
return 1;
|
|
}
|
|
|
|
return _size32_disp(rh, mem, field, &lv->read_ahead, private);
|
|
}
|
|
|
|
static int _lvkreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data,
|
|
void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
|
|
if (!lv_info(lv->vg->cmd, lv, &info, 0, 1) || !info.exists)
|
|
return dm_report_field_int32(rh, field, &_minusone32);
|
|
|
|
return _size32_disp(rh, mem, field, &info.read_ahead, private);
|
|
}
|
|
|
|
static int _vgsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t size;
|
|
|
|
size = (uint64_t) vg_size(vg);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t start;
|
|
|
|
start = (uint64_t) seg->le * seg->lv->vg->extent_size;
|
|
|
|
return _size64_disp(rh, mem, field, &start, private);
|
|
}
|
|
|
|
static int _segstartpe_disp(struct dm_report *rh,
|
|
struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data,
|
|
void *private __attribute((unused)))
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
|
|
return dm_report_field_uint32(rh, field, &seg->le);
|
|
}
|
|
|
|
static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t size;
|
|
|
|
size = (uint64_t) seg->len * seg->lv->vg->extent_size;
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t size;
|
|
|
|
if (lv_is_cow(seg->lv))
|
|
size = (uint64_t) find_cow(seg->lv)->chunk_size;
|
|
else
|
|
size = UINT64_C(0);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
uint64_t size;
|
|
|
|
if (lv_is_cow(lv))
|
|
size = (uint64_t) find_cow(lv)->len * lv->vg->extent_size;
|
|
else if (lv_is_origin(lv))
|
|
size = lv->size;
|
|
else
|
|
size = UINT64_C(0);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _pvused_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t used;
|
|
|
|
if (!pv->pe_count)
|
|
used = 0LL;
|
|
else
|
|
used = (uint64_t) pv->pe_alloc_count * pv->pe_size;
|
|
|
|
return _size64_disp(rh, mem, field, &used, private);
|
|
}
|
|
|
|
static int _pvfree_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t freespace;
|
|
|
|
freespace = pv_free(pv);
|
|
|
|
return _size64_disp(rh, mem, field, &freespace, private);
|
|
}
|
|
|
|
static int _pvsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t size;
|
|
|
|
size = pv_size_field(pv);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _devsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t size;
|
|
|
|
size = pv_dev_size(pv);
|
|
|
|
return _size64_disp(rh, mem, field, &size, private);
|
|
}
|
|
|
|
static int _vgfree_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t freespace;
|
|
|
|
freespace = (uint64_t) vg_free(vg);
|
|
|
|
return _size64_disp(rh, mem, field, &freespace, private);
|
|
}
|
|
|
|
static int _uuid_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
char *repstr = NULL;
|
|
|
|
if (!(repstr = dm_pool_alloc(mem, 40))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!id_write_format((const struct id *) data, repstr, 40))
|
|
return_0;
|
|
|
|
dm_report_field_set_value(field, repstr, NULL);
|
|
return 1;
|
|
}
|
|
|
|
static int _uint32_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
return dm_report_field_uint32(rh, field, data);
|
|
}
|
|
|
|
static int _int32_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
return dm_report_field_int32(rh, field, data);
|
|
}
|
|
|
|
static int _pvmdas_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
uint32_t count;
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
|
|
count = pv_mda_count(pv);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _pvmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
uint32_t count;
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
|
|
count = pv_mda_used_count(pv);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _vgmdas_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint32_t count;
|
|
|
|
count = vg_mda_count(vg);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _vgmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint32_t count;
|
|
|
|
count = vg_mda_used_count(vg);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _vgmdacopies_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint32_t count;
|
|
|
|
count = vg_mda_copies(vg);
|
|
|
|
if (count == VGMETADATACOPIES_UNMANAGED) {
|
|
dm_report_field_set_value(field, "unmanaged", &_minusone64);
|
|
return 1;
|
|
}
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _pvmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct lvmcache_info *info;
|
|
uint64_t freespace = UINT64_MAX, mda_free;
|
|
const char *pvid = (const char *)(&((const struct id *) data)->uuid);
|
|
struct metadata_area *mda;
|
|
|
|
if ((info = info_from_pvid(pvid, 0)))
|
|
dm_list_iterate_items(mda, &info->mdas) {
|
|
if (!mda->ops->mda_free_sectors)
|
|
continue;
|
|
mda_free = mda->ops->mda_free_sectors(mda);
|
|
if (mda_free < freespace)
|
|
freespace = mda_free;
|
|
}
|
|
|
|
if (freespace == UINT64_MAX)
|
|
freespace = UINT64_C(0);
|
|
|
|
return _size64_disp(rh, mem, field, &freespace, private);
|
|
}
|
|
|
|
static uint64_t _find_min_mda_size(struct dm_list *mdas)
|
|
{
|
|
uint64_t min_mda_size = UINT64_MAX, mda_size;
|
|
struct metadata_area *mda;
|
|
|
|
dm_list_iterate_items(mda, mdas) {
|
|
if (!mda->ops->mda_total_sectors)
|
|
continue;
|
|
mda_size = mda->ops->mda_total_sectors(mda);
|
|
if (mda_size < min_mda_size)
|
|
min_mda_size = mda_size;
|
|
}
|
|
|
|
if (min_mda_size == UINT64_MAX)
|
|
min_mda_size = UINT64_C(0);
|
|
|
|
return min_mda_size;
|
|
}
|
|
|
|
static int _pvmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
struct lvmcache_info *info;
|
|
uint64_t min_mda_size = 0;
|
|
const char *pvid = (const char *)(&((const struct id *) data)->uuid);
|
|
|
|
/* PVs could have 2 mdas of different sizes (rounding effect) */
|
|
if ((info = info_from_pvid(pvid, 0)))
|
|
min_mda_size = _find_min_mda_size(&info->mdas);
|
|
|
|
return _size64_disp(rh, mem, field, &min_mda_size, private);
|
|
}
|
|
|
|
static int _vgmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t min_mda_size;
|
|
|
|
min_mda_size = _find_min_mda_size(&vg->fid->metadata_areas_in_use);
|
|
|
|
return _size64_disp(rh, mem, field, &min_mda_size, private);
|
|
}
|
|
|
|
static int _vgmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t freespace = UINT64_MAX, mda_free;
|
|
struct metadata_area *mda;
|
|
|
|
dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
|
|
if (!mda->ops->mda_free_sectors)
|
|
continue;
|
|
mda_free = mda->ops->mda_free_sectors(mda);
|
|
if (mda_free < freespace)
|
|
freespace = mda_free;
|
|
}
|
|
|
|
if (freespace == UINT64_MAX)
|
|
freespace = UINT64_C(0);
|
|
|
|
return _size64_disp(rh, mem, field, &freespace, private);
|
|
}
|
|
|
|
static int _lvcount_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint32_t count;
|
|
|
|
count = vg_visible_lvs(vg);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _lvsegcount_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
uint32_t count;
|
|
|
|
count = dm_list_size(&lv->segments);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _snapcount_disp(struct dm_report *rh, struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint32_t count;
|
|
|
|
count = snapshot_count(vg);
|
|
|
|
return _uint32_disp(rh, mem, field, &count, private);
|
|
}
|
|
|
|
static int _snpercent_disp(struct dm_report *rh __attribute((unused)), struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
float snap_percent;
|
|
percent_range_t percent_range;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
/* Suppress snapshot percentage if not using driver */
|
|
if (!activation()) {
|
|
dm_report_field_set_value(field, "", NULL);
|
|
return 1;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if ((!lv_is_cow(lv) && !lv_is_merging_origin(lv)) ||
|
|
!lv_info(lv->vg->cmd, lv, &info, 0, 0) || !info.exists) {
|
|
*sortval = UINT64_C(0);
|
|
dm_report_field_set_value(field, "", sortval);
|
|
return 1;
|
|
}
|
|
|
|
if (!lv_snapshot_percent(lv, &snap_percent, &percent_range) ||
|
|
(percent_range == PERCENT_INVALID)) {
|
|
if (!lv_is_merging_origin(lv)) {
|
|
*sortval = UINT64_C(100);
|
|
dm_report_field_set_value(field, "100.00", sortval);
|
|
} else {
|
|
/* onactivate merge that hasn't started yet would
|
|
* otherwise display incorrect snap% in origin
|
|
*/
|
|
*sortval = UINT64_C(0);
|
|
dm_report_field_set_value(field, "", sortval);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, 8))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 7, "%.2f", snap_percent) < 0) {
|
|
log_error("snapshot percentage too large");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (uint64_t)(snap_percent * 1000.f);
|
|
dm_report_field_set_value(field, repstr, sortval);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _copypercent_disp(struct dm_report *rh __attribute((unused)),
|
|
struct dm_pool *mem,
|
|
struct dm_report_field *field,
|
|
const void *data, void *private __attribute((unused)))
|
|
{
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
float percent;
|
|
percent_range_t percent_range;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if ((!(lv->status & PVMOVE) && !(lv->status & MIRRORED)) ||
|
|
!lv_mirror_percent(lv->vg->cmd, lv, 0, &percent, &percent_range,
|
|
NULL) || (percent_range == PERCENT_INVALID)) {
|
|
*sortval = UINT64_C(0);
|
|
dm_report_field_set_value(field, "", sortval);
|
|
return 1;
|
|
}
|
|
|
|
percent = copy_percent(lv, &percent_range);
|
|
|
|
if (!(repstr = dm_pool_zalloc(mem, 8))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 7, "%.2f", percent) < 0) {
|
|
log_error("copy percentage too large");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (uint64_t)(percent * 1000.f);
|
|
dm_report_field_set_value(field, repstr, sortval);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Report object types */
|
|
|
|
/* necessary for displaying something for PVs not belonging to VG */
|
|
static struct format_instance _dummy_fid = {
|
|
.metadata_areas_in_use = { &(_dummy_fid.metadata_areas_in_use), &(_dummy_fid.metadata_areas_in_use) },
|
|
.metadata_areas_ignored = { &(_dummy_fid.metadata_areas_ignored), &(_dummy_fid.metadata_areas_ignored) },
|
|
};
|
|
|
|
static struct volume_group _dummy_vg = {
|
|
.fid = &_dummy_fid,
|
|
.name = (char *) "",
|
|
.system_id = (char *) "",
|
|
.pvs = { &(_dummy_vg.pvs), &(_dummy_vg.pvs) },
|
|
.lvs = { &(_dummy_vg.lvs), &(_dummy_vg.lvs) },
|
|
.tags = { &(_dummy_vg.tags), &(_dummy_vg.tags) },
|
|
};
|
|
|
|
static void *_obj_get_vg(void *obj)
|
|
{
|
|
struct volume_group *vg = ((struct lvm_report_object *)obj)->vg;
|
|
|
|
return vg ? vg : &_dummy_vg;
|
|
}
|
|
|
|
static void *_obj_get_lv(void *obj)
|
|
{
|
|
return ((struct lvm_report_object *)obj)->lv;
|
|
}
|
|
|
|
static void *_obj_get_pv(void *obj)
|
|
{
|
|
return ((struct lvm_report_object *)obj)->pv;
|
|
}
|
|
|
|
static void *_obj_get_seg(void *obj)
|
|
{
|
|
return ((struct lvm_report_object *)obj)->seg;
|
|
}
|
|
|
|
static void *_obj_get_pvseg(void *obj)
|
|
{
|
|
return ((struct lvm_report_object *)obj)->pvseg;
|
|
}
|
|
|
|
static const struct dm_report_object_type _report_types[] = {
|
|
{ VGS, "Volume Group", "vg_", _obj_get_vg },
|
|
{ LVS, "Logical Volume", "lv_", _obj_get_lv },
|
|
{ PVS, "Physical Volume", "pv_", _obj_get_pv },
|
|
{ LABEL, "Physical Volume Label", "pv_", _obj_get_pv },
|
|
{ SEGS, "Logical Volume Segment", "seg_", _obj_get_seg },
|
|
{ PVSEGS, "Physical Volume Segment", "pvseg_", _obj_get_pvseg },
|
|
{ 0, "", "", NULL },
|
|
};
|
|
|
|
/*
|
|
* Import column definitions
|
|
*/
|
|
|
|
#define STR DM_REPORT_FIELD_TYPE_STRING
|
|
#define NUM DM_REPORT_FIELD_TYPE_NUMBER
|
|
#define FIELD(type, strct, sorttype, head, field, width, func, id, desc) \
|
|
{type, sorttype, offsetof(type_ ## strct, field), width, \
|
|
id, head, &_ ## func ## _disp, desc},
|
|
|
|
typedef struct physical_volume type_pv;
|
|
typedef struct logical_volume type_lv;
|
|
typedef struct volume_group type_vg;
|
|
typedef struct lv_segment type_seg;
|
|
typedef struct pv_segment type_pvseg;
|
|
|
|
static const struct dm_report_field_type _fields[] = {
|
|
#include "columns.h"
|
|
{0, 0, 0, 0, "", "", NULL, NULL},
|
|
};
|
|
|
|
#undef STR
|
|
#undef NUM
|
|
#undef FIELD
|
|
|
|
void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
|
|
report_type_t *report_type, const char *separator,
|
|
int aligned, int buffered, int headings, int field_prefixes,
|
|
int quoted, int columns_as_rows)
|
|
{
|
|
uint32_t report_flags = 0;
|
|
void *rh;
|
|
|
|
if (aligned)
|
|
report_flags |= DM_REPORT_OUTPUT_ALIGNED;
|
|
|
|
if (buffered)
|
|
report_flags |= DM_REPORT_OUTPUT_BUFFERED;
|
|
|
|
if (headings)
|
|
report_flags |= DM_REPORT_OUTPUT_HEADINGS;
|
|
|
|
if (field_prefixes)
|
|
report_flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
|
|
|
|
if (!quoted)
|
|
report_flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
|
|
|
|
if (columns_as_rows)
|
|
report_flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
|
|
|
|
rh = dm_report_init(report_type, _report_types, _fields, format,
|
|
separator, report_flags, keys, cmd);
|
|
|
|
if (rh && field_prefixes)
|
|
dm_report_set_output_field_name_prefix(rh, "lvm2_");
|
|
|
|
return rh;
|
|
}
|
|
|
|
/*
|
|
* Create a row of data for an object
|
|
*/
|
|
int report_object(void *handle, struct volume_group *vg,
|
|
struct logical_volume *lv, struct physical_volume *pv,
|
|
struct lv_segment *seg, struct pv_segment *pvseg)
|
|
{
|
|
struct lvm_report_object obj;
|
|
|
|
/* The two format fields might as well match. */
|
|
if (!vg && pv)
|
|
_dummy_fid.fmt = pv->fmt;
|
|
|
|
obj.vg = vg;
|
|
obj.lv = lv;
|
|
obj.pv = pv;
|
|
obj.seg = seg;
|
|
obj.pvseg = pvseg;
|
|
|
|
return dm_report_object(handle, &obj);
|
|
}
|