From cf6dd251261f9fb273c39a09fcb6863cdc13cd07 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Mon, 8 Mar 2004 17:19:15 +0000 Subject: [PATCH] tagging --- lib/Makefile.in | 1 + lib/format1/format1.c | 1 + lib/format1/import-export.c | 11 +- lib/format_text/export.c | 50 ++- lib/format_text/format-text.c | 2 +- lib/format_text/import-export.h | 3 + lib/format_text/import_vsn1.c | 35 ++ lib/format_text/tags.c | 67 ++++ lib/metadata/lv_manip.c | 3 + lib/metadata/merge.c | 9 + lib/metadata/metadata.c | 7 + lib/metadata/metadata.h | 7 + lib/report/columns.h | 4 + lib/report/report.c | 30 ++ tools/args.h | 2 + tools/commands.h | 30 +- tools/lvchange.c | 90 ++++-- tools/lvcreate.c | 25 +- tools/lvm.c | 14 + tools/pvchange.c | 44 ++- tools/toollib.c | 551 +++++++++++++++++++++----------- tools/toollib.h | 3 +- tools/tools.h | 2 + tools/vgchange.c | 62 +++- tools/vgcreate.c | 19 ++ tools/vgdisplay.c | 5 +- 26 files changed, 841 insertions(+), 236 deletions(-) create mode 100644 lib/format_text/tags.c diff --git a/lib/Makefile.in b/lib/Makefile.in index cbe6c96ac..dfa73bf36 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -36,6 +36,7 @@ SOURCES=\ format_text/format-text.c \ format_text/import.c \ format_text/import_vsn1.c \ + format_text/tags.c \ format_text/text_label.c \ label/label.c \ locking/file_locking.c \ diff --git a/lib/format1/format1.c b/lib/format1/format1.c index ec51c3ec6..7f3c4e5a3 100644 --- a/lib/format1/format1.c +++ b/lib/format1/format1.c @@ -139,6 +139,7 @@ static struct volume_group *_build_vg(struct format_instance *fid, list_init(&vg->pvs); list_init(&vg->lvs); list_init(&vg->snapshots); + list_init(&vg->tags); if (!_check_vgs(pvs, &partial)) goto bad; diff --git a/lib/format1/import-export.c b/lib/format1/import-export.c index bc67c8169..0bbb963db 100644 --- a/lib/format1/import-export.c +++ b/lib/format1/import-export.c @@ -56,9 +56,9 @@ int import_pv(struct pool *mem, struct device *dev, if (vg && strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id))) - log_very_verbose("System ID %s on %s differs from %s for " - "volume group", pvd->system_id, - dev_name(pv->dev), vg->system_id); + log_very_verbose("System ID %s on %s differs from %s for " + "volume group", pvd->system_id, + dev_name(pv->dev), vg->system_id); /* * If exported, we still need to flag in pv->status too because @@ -76,6 +76,8 @@ int import_pv(struct pool *mem, struct device *dev, pv->pe_count = pvd->pe_total; pv->pe_alloc_count = pvd->pe_allocated; + list_init(&pv->tags); + return 1; } @@ -162,7 +164,7 @@ int export_pv(struct pool *mem, struct volume_group *vg, if (vg && (!*vg->system_id || strncmp(vg->system_id, pvd->system_id, sizeof(pvd->system_id)))) - strncpy(vg->system_id, pvd->system_id, NAME_LEN); + strncpy(vg->system_id, pvd->system_id, NAME_LEN); //pvd->pv_major = MAJOR(pv->dev); @@ -314,6 +316,7 @@ int import_lv(struct pool *mem, struct logical_volume *lv, struct lv_disk *lvd) lv->le_count = lvd->lv_allocated_le; list_init(&lv->segments); + list_init(&lv->tags); return 1; } diff --git a/lib/format_text/export.c b/lib/format_text/export.c index 0396eaa0f..9552dd913 100644 --- a/lib/format_text/export.c +++ b/lib/format_text/export.c @@ -270,7 +270,7 @@ static int _print_header(struct formatter *f, static int _print_vg(struct formatter *f, struct volume_group *vg) { - char buffer[256]; + char buffer[4096]; if (!id_write_format(&vg->id, buffer, sizeof(buffer))) { stack; @@ -280,14 +280,24 @@ static int _print_vg(struct formatter *f, struct volume_group *vg) _outf(f, "id = \"%s\"", buffer); _outf(f, "seqno = %u", vg->seqno); + if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } - _outf(f, "status = %s", buffer); + + if (!list_empty(&vg->tags)) { + if (!print_tags(&vg->tags, buffer, sizeof(buffer))) { + stack; + return 0; + } + _outf(f, "tags = %s", buffer); + } + if (vg->system_id && *vg->system_id) _outf(f, "system_id = \"%s\"", vg->system_id); + if (!_out_size(f, (uint64_t) vg->extent_size, "extent_size = %u", vg->extent_size)) { stack; @@ -314,7 +324,7 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) { struct list *pvh; struct physical_volume *pv; - char buffer[256]; + char buffer[4096]; const char *name; _outf(f, "physical_volumes {"); @@ -348,8 +358,16 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) stack; return 0; } - _outf(f, "status = %s", buffer); + + if (!list_empty(&pv->tags)) { + if (!print_tags(&pv->tags, buffer, sizeof(buffer))) { + stack; + return 0; + } + _outf(f, "tags = %s", buffer); + } + _outf(f, "pe_start = %" PRIu64, pv->pe_start); if (!_out_size(f, vg->extent_size * (uint64_t) pv->pe_count, "pe_count = %u", pv->pe_count)) { @@ -372,6 +390,7 @@ static int _print_segment(struct formatter *f, struct volume_group *vg, unsigned int s; const char *name; const char *type; + char buffer[4096]; _outf(f, "segment%u {", count); _inc_indent(f); @@ -386,6 +405,14 @@ static int _print_segment(struct formatter *f, struct volume_group *vg, f->nl(f); _outf(f, "type = \"%s\"", get_segtype_string(seg->type)); + if (!list_empty(&seg->tags)) { + if (!print_tags(&seg->tags, buffer, sizeof(buffer))) { + stack; + return 0; + } + _outf(f, "tags = %s", buffer); + } + switch (seg->type) { case SEG_SNAPSHOT: _outf(f, "chunk_size = %u", seg->chunk_size); @@ -490,6 +517,9 @@ static int _print_snapshot(struct formatter *f, struct snapshot *snap, seg.cow = snap->cow; seg.chunk_size = snap->chunk_size; + /* Can't tag a snapshot independently of its origin */ + list_init(&seg.tags); + if (!_print_segment(f, snap->origin->vg, 1, &seg)) { stack; return 0; @@ -524,7 +554,7 @@ static int _print_lvs(struct formatter *f, struct volume_group *vg) struct list *lvh; struct logical_volume *lv; struct lv_segment *seg; - char buffer[256]; + char buffer[4096]; int seg_count; /* @@ -555,8 +585,16 @@ static int _print_lvs(struct formatter *f, struct volume_group *vg) stack; return 0; } - _outf(f, "status = %s", buffer); + + if (!list_empty(&lv->tags)) { + if (!print_tags(&lv->tags, buffer, sizeof(buffer))) { + stack; + return 0; + } + _outf(f, "tags = %s", buffer); + } + if (lv->alloc != ALLOC_DEFAULT) _outf(f, "allocation_policy = \"%s\"", get_alloc_string(lv->alloc)); diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c index a652d8a23..0d5b8548c 100644 --- a/lib/format_text/format-text.c +++ b/lib/format_text/format-text.c @@ -1640,7 +1640,7 @@ struct format_type *create_text_format(struct cmd_context *cmd) fmt->ops = &_text_handler; fmt->name = FMT_TEXT_NAME; fmt->alias = FMT_TEXT_ALIAS; - fmt->features = FMT_SEGMENTS | FMT_MDAS; + fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS; if (!(mda_lists = dbg_malloc(sizeof(struct mda_lists)))) { log_error("Failed to allocate dir_list"); diff --git a/lib/format_text/import-export.h b/lib/format_text/import-export.h index 87ec5c771..54ee4b821 100644 --- a/lib/format_text/import-export.h +++ b/lib/format_text/import-export.h @@ -46,6 +46,9 @@ struct text_vg_version_ops *text_vg_vsn1_init(void); int print_flags(uint32_t status, int type, char *buffer, size_t size); int read_flags(uint32_t *status, int type, struct config_value *cv); +int print_tags(struct list *tags, char *buffer, size_t size); +int read_tags(struct pool *mem, struct list *tags, struct config_value *cv); + int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp); int text_vg_export_raw(struct volume_group *vg, const char *desc, char *buf, uint32_t size); diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c index 0b3311e8c..008026dc7 100644 --- a/lib/format_text/import_vsn1.c +++ b/lib/format_text/import_vsn1.c @@ -178,6 +178,16 @@ static int _read_pv(struct format_instance *fid, struct pool *mem, return 0; } + list_init(&pv->tags); + + /* Optional tags */ + if ((cn = find_config_node(pvn, "tags", '/')) && + !(read_tags(mem, &pv->tags, cn->v))) { + log_error("Couldn't read tags for physical volume %s in %s.", + dev_name(pv->dev), vg->name); + return 0; + } + /* adjust the volume group. */ vg->extent_count += pv->pe_count; vg->free_count += pv->pe_count; @@ -293,6 +303,14 @@ static int _read_segment(struct pool *mem, struct volume_group *vg, seg->status = seg_status; seg->extents_moved = extents_moved; + /* Optional tags */ + if ((cn = find_config_node(sn, "tags", '/')) && + !(read_tags(mem, &seg->tags, cn->v))) { + log_error("Couldn't read tags for a segment of %s/%s.", + vg->name, lv->name); + return 0; + } + switch (segtype) { case SEG_SNAPSHOT: lv->status |= SNAPSHOT; @@ -544,6 +562,15 @@ static int _read_lvnames(struct format_instance *fid, struct pool *mem, lv->read_ahead = 0; list_init(&lv->segments); + list_init(&lv->tags); + + /* Optional tags */ + if ((cn = find_config_node(lvn, "tags", '/')) && + !(read_tags(mem, &lv->tags, cn->v))) { + log_error("Couldn't read tags for logical volume %s/%s.", + vg->name, lv->name); + return 0; + } lv->vg = vg; vg->lv_count++; @@ -748,6 +775,14 @@ static struct volume_group *_read_vg(struct format_instance *fid, list_init(&vg->lvs); list_init(&vg->snapshots); + list_init(&vg->tags); + + /* Optional tags */ + if ((cn = find_config_node(vgn, "tags", '/')) && + !(read_tags(mem, &vg->tags, cn->v))) { + log_error("Couldn't read tags for volume group %s.", vg->name); + goto bad; + } if (!_read_sections(fid, "logical_volumes", _read_lvnames, mem, vg, vgn, pv_hash, 1)) { diff --git a/lib/format_text/tags.c b/lib/format_text/tags.c new file mode 100644 index 000000000..88f96d752 --- /dev/null +++ b/lib/format_text/tags.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "lib.h" +#include "metadata.h" +#include "import-export.h" +#include "pool.h" +#include "str_list.h" +#include "lvm-string.h" + +int print_tags(struct list *tags, char *buffer, size_t size) +{ + struct str_list *sl; + int first = 1; + + if (!emit_to_buffer(&buffer, &size, "[")) { + stack; + return 0; + } + + list_iterate_items(sl, tags) { + if (!first) { + if (!emit_to_buffer(&buffer, &size, ", ")) { + stack; + return 0; + } + } else + first = 0; + + if (!emit_to_buffer(&buffer, &size, "\"%s\"", sl->str)) { + stack; + return 0; + } + } + + if (!emit_to_buffer(&buffer, &size, "]")) { + stack; + return 0; + } + + return 1; +} + +int read_tags(struct pool *mem, struct list *tags, struct config_value *cv) +{ + if (cv->type == CFG_EMPTY_ARRAY) + return 1; + + while (cv) { + if (cv->type != CFG_STRING) { + log_error("Found a tag that is not a string"); + return 0; + } + + if (!str_list_add(mem, tags, pool_strdup(mem, cv->v.str))) { + stack; + return 0; + } + + cv = cv->next; + } + + return 1; +} diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index e72abae12..968562adc 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -60,6 +60,8 @@ struct lv_segment *alloc_lv_segment(struct pool *mem, uint32_t stripes) return NULL; } + list_init(&seg->tags); + return seg; } @@ -519,6 +521,7 @@ struct logical_volume *lv_create_empty(struct format_instance *fi, lv->size = UINT64_C(0); lv->le_count = 0; list_init(&lv->segments); + list_init(&lv->tags); if (fi->fmt->ops->lv_setup && !fi->fmt->ops->lv_setup(fi, lv)) { stack; diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c index 286a92b8c..ed628dca1 100644 --- a/lib/metadata/merge.c +++ b/lib/metadata/merge.c @@ -8,6 +8,7 @@ #include "metadata.h" #include "toolcontext.h" #include "lv_alloc.h" +#include "str_list.h" /* * Test whether two segments could be merged by the current merging code @@ -40,6 +41,9 @@ static int _segments_compatible(struct lv_segment *first, return 0; } + if (!str_list_lists_equal(&first->tags, &second->tags)) + return 0; + return 1; } @@ -129,6 +133,11 @@ static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg, len = sizeof(*seg) + (seg->area_count * sizeof(seg->area[0])); memcpy(split_seg, seg, len); + if (!str_list_dup(lv->vg->cmd->mem, &split_seg->tags, &seg->tags)) { + log_error("LV segment tags duplication failed"); + return 0; + } + /* In case of a striped segment, the offset has to be / stripes */ if (seg->type == SEG_STRIPED) offset /= seg->area_count; diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index bf92ec139..c16527135 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -211,6 +211,8 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name, vg->snapshot_count = 0; list_init(&vg->snapshots); + list_init(&vg->tags); + if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg_name, NULL))) { log_error("Failed to create format instance"); @@ -293,6 +295,8 @@ struct physical_volume *pv_create(const struct format_type *fmt, pv->pe_alloc_count = 0; pv->fmt = fmt; + list_init(&pv->tags); + if (!fmt->ops->pv_setup(fmt, pe_start, existing_extent_count, existing_extent_size, pvmetadatacopies, pvmetadatasize, mdas, @@ -562,6 +566,7 @@ static struct volume_group *_vg_read_orphans(struct cmd_context *cmd) list_init(&vg->pvs); list_init(&vg->lvs); list_init(&vg->snapshots); + list_init(&vg->tags); vg->cmd = cmd; if (!(vg->name = pool_strdup(cmd->mem, ORPHAN))) { log_error("vg name allocation failed"); @@ -805,6 +810,8 @@ struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name, return 0; } + list_init(&pv->tags); + /* FIXME Move more common code up here */ if (!(info->fmt->ops->pv_read(info->fmt, pv_name, pv, mdas))) { log_error("Failed to read existing physical volume '%s'", diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 3d476e78b..e2f633229 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -103,6 +103,8 @@ struct physical_volume { uint64_t pe_start; uint32_t pe_count; uint32_t pe_alloc_count; + + struct list tags; }; struct metadata_area; @@ -177,6 +179,8 @@ struct volume_group { /* snapshots */ uint32_t snapshot_count; struct list snapshots; + + struct list tags; }; struct lv_segment { @@ -198,6 +202,8 @@ struct lv_segment { uint32_t chunk_size; uint32_t extents_moved; + struct list tags; + /* There will be one area for each stripe */ struct { area_type_t type; @@ -230,6 +236,7 @@ struct logical_volume { uint32_t le_count; struct list segments; + struct list tags; }; struct snapshot { diff --git a/lib/report/columns.h b/lib/report/columns.h index 2526fcd70..8613eea90 100644 --- a/lib/report/columns.h +++ b/lib/report/columns.h @@ -20,6 +20,7 @@ FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin") FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, "snap_percent") FIELD(LVS, lv, NUM, "Move%", lvid, 6, movepercent, "move_percent") FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv") +FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, "lv_tags") FIELD(PVS, pv, STR, "Fmt", id, 3, pvfmt, "pv_fmt") FIELD(PVS, pv, STR, "PV UUID", id, 38, uuid, "pv_uuid") @@ -30,6 +31,7 @@ FIELD(PVS, pv, STR, "PV", dev, 10, dev_name, "pv_name") FIELD(PVS, pv, STR, "Attr", status, 4, pvstatus, "pv_attr") FIELD(PVS, pv, NUM, "PE", pe_count, 3, uint32, "pv_pe_count") FIELD(PVS, pv, NUM, "Alloc", pe_alloc_count, 5, uint32, "pv_pe_alloc_count") +FIELD(PVS, pv, STR, "PV Tags", tags, 7, tags, "pv_tags") FIELD(VGS, vg, STR, "Fmt", cmd, 3, vgfmt, "vg_fmt") FIELD(VGS, vg, STR, "VG UUID", id, 38, uuid, "vg_uuid") @@ -47,6 +49,7 @@ FIELD(VGS, vg, NUM, "#PV", pv_count, 3, uint32, "pv_count") FIELD(VGS, vg, NUM, "#LV", lv_count, 3, uint32, "lv_count") FIELD(VGS, vg, NUM, "#SN", snapshot_count, 3, uint32, "snap_count") FIELD(VGS, vg, NUM, "Seq", seqno, 3, uint32, "vg_seqno") +FIELD(VGS, vg, STR, "VG Tags", tags, 7, tags, "vg_tags") FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype") FIELD(SEGS, seg, NUM, "#Str", area_count, 4, uint32, "stripes") @@ -54,4 +57,5 @@ FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripesize") FIELD(SEGS, seg, NUM, "Chunk", chunk_size, 5, size32, "chunksize") FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, "seg_start") FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, "seg_size") +FIELD(SEGS, seg, STR, "Seg Tags", tags, 8, tags, "seg_tags") /* *INDENT-ON* */ diff --git a/lib/report/report.c b/lib/report/report.c index 97adeb900..054b3a486 100644 --- a/lib/report/report.c +++ b/lib/report/report.c @@ -128,6 +128,36 @@ static int _dev_name_disp(struct report_handle *rh, struct field *field, return _string_disp(rh, field, &name); } +static int _tags_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + const struct list *tags = (const struct list *) data; + struct str_list *sl; + + if (!pool_begin_object(rh->mem, 256)) { + log_error("pool_begin_object failed"); + return 0; + } + + list_iterate_items(sl, tags) { + if (!pool_grow_object(rh->mem, sl->str, strlen(sl->str)) || + (sl->list.n != tags && !pool_grow_object(rh->mem, ",", 1))) { + log_error("pool_grow_object failed"); + return 0; + } + } + + if (!pool_grow_object(rh->mem, "\0", 1)) { + log_error("pool_grow_object failed"); + return 0; + } + + field->report_string = pool_end_object(rh->mem); + field->sort_value = (const void *) field->report_string; + + return 1; +} + static int _vgfmt_disp(struct report_handle *rh, struct field *field, const void *data) { diff --git a/tools/args.h b/tools/args.h index 2297c5508..4c3da4a78 100644 --- a/tools/args.h +++ b/tools/args.h @@ -26,6 +26,8 @@ arg(units_ARG, '\0', "units", string_arg) arg(nosuffix_ARG, '\0', "nosuffix", NULL) arg(removemissing_ARG, '\0', "removemissing", NULL) arg(abort_ARG, '\0', "abort", NULL) +arg(addtag_ARG, '\0', "addtag", tag_arg) +arg(deltag_ARG, '\0', "deltag", tag_arg) arg(mknodes_ARG, '\0', "mknodes", NULL) arg(minor_ARG, '\0', "minor", minor_arg) diff --git a/tools/commands.h b/tools/commands.h index 94b76be52..af8869c97 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -56,8 +56,10 @@ xx(lvchange, "lvchange\n" "\t[-A|--autobackup y|n]\n" "\t[-a|--available y|n]\n" + "\t[--addtag Tag]\n" "\t[-C|--contiguous y|n]\n" "\t[-d|--debug]\n" + "\t[--deltag Tag]\n" "\t[-f|--force]\n" "\t[-h|--help]\n" "\t[--ignorelockingfailure]\n" @@ -72,12 +74,14 @@ xx(lvchange, autobackup_ARG, available_ARG, contiguous_ARG, force_ARG, ignorelockingfailure_ARG, major_ARG, minor_ARG, partial_ARG, permission_ARG, - persistent_ARG, readahead_ARG, test_ARG) + persistent_ARG, readahead_ARG, addtag_ARG, deltag_ARG, + test_ARG) xx(lvcreate, "Create a logical volume", "lvcreate " "\n" "\t[-A|--autobackup {y|n}]\n" + "\t[--addtag Tag]\n" "\t[-C|--contiguous {y|n}]\n" "\t[-d|--debug]\n" "\t[-h|-?|--help]\n" @@ -97,6 +101,7 @@ xx(lvcreate, "lvcreate -s|--snapshot\n" "\t[-c|--chunksize]\n" "\t[-A|--autobackup {y|n}]\n" + "\t[--addtag Tag]\n" "\t[-C|--contiguous {y|n}]\n" "\t[-d|--debug]\n" "\t[-h|-?|--help]\n" @@ -112,9 +117,10 @@ xx(lvcreate, "\t[--version]\n" "\tOriginalLogicalVolume[Path] [PhysicalVolumePath...]\n\n", - autobackup_ARG, chunksize_ARG, contiguous_ARG, extents_ARG, major_ARG, minor_ARG, - name_ARG, permission_ARG, persistent_ARG, readahead_ARG, size_ARG, - snapshot_ARG, stripes_ARG, stripesize_ARG, test_ARG, zero_ARG) + autobackup_ARG, chunksize_ARG, contiguous_ARG, extents_ARG, major_ARG, + minor_ARG, name_ARG, permission_ARG, persistent_ARG, readahead_ARG, size_ARG, + snapshot_ARG, stripes_ARG, stripesize_ARG, addtag_ARG, test_ARG, + zero_ARG) xx(lvdisplay, "Display information about a logical volume", @@ -323,10 +329,13 @@ xx(pvchange, "\t[-u|--uuid]\n" "\t[-x|--allocatable y|n]\n" "\t[-v|--verbose]\n" + "\t[--addtag Tag]\n" + "\t[--deltag Tag]\n" "\t[--version]" "\n" "\t[PhysicalVolumePath...]\n", - all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, test_ARG, uuid_ARG) + all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, deltag_ARG, + addtag_ARG, test_ARG, uuid_ARG) xx(pvcreate, "Initialize physical volume(s) for use by LVM", @@ -537,12 +546,14 @@ xx(vgchange, "\t[--version]" "\n" "\t{-a|--available {y|n} |" "\n" "\t -x|--resizeable {y|n} |" "\n" - "\t -l|--logicalvolume MaxLogicalVolumes}" "\n" + "\t -l|--logicalvolume MaxLogicalVolumes |" "\n" + "\t --addtag Tag |\n" + "\t --deltag Tag}\n" "\t[VolumeGroupName...]\n", allocation_ARG, autobackup_ARG, available_ARG, ignorelockingfailure_ARG, - logicalvolume_ARG, partial_ARG, resizeable_ARG, resizable_ARG, test_ARG, - uuid_ARG) + logicalvolume_ARG, partial_ARG, resizeable_ARG, resizable_ARG, deltag_ARG, + addtag_ARG, test_ARG, uuid_ARG) xx(vgck, "Check the consistency of volume group(s)", @@ -574,6 +585,7 @@ xx(vgcreate, "Create a volume group", "vgcreate" "\n" "\t[-A|--autobackup {y|n}] " "\n" + "\t[--addtag Tag] " "\n" "\t[-d|--debug]" "\n" "\t[-h|--help]" "\n" "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n" @@ -586,7 +598,7 @@ xx(vgcreate, "\tVolumeGroupName PhysicalVolume [PhysicalVolume...]\n", autobackup_ARG, maxlogicalvolumes_ARG, maxphysicalvolumes_ARG, - metadatatype_ARG, physicalextentsize_ARG, test_ARG) + metadatatype_ARG, physicalextentsize_ARG, addtag_ARG, test_ARG) xx(vgdisplay, "Display volume group information", diff --git a/tools/lvchange.c b/tools/lvchange.c index f20f1df2a..75cfa2195 100644 --- a/tools/lvchange.c +++ b/tools/lvchange.c @@ -151,19 +151,9 @@ static int lvchange_contiguous(struct cmd_context *cmd, backup(lv->vg); - if (!lock_vol(cmd, lv->lvid.s, LCK_LV_SUSPEND | LCK_HOLD)) { - log_error("Failed to lock %s", lv->name); - vg_revert(lv->vg); - return 0; - } - + /* No need to suspend LV for this change */ if (!vg_commit(lv->vg)) { - unlock_lv(cmd, lv->lvid.s); - return 0; - } - - if (!unlock_lv(cmd, lv->lvid.s)) { - log_error("Problem reactivating %s", lv->name); + stack; return 0; } @@ -249,8 +239,8 @@ static int lvchange_persistent(struct cmd_context *cmd, log_error("Major number must be specified with -My"); return 0; } - if (lv_info(lv, &info) && info.exists && - !arg_count(cmd, force_ARG)) { + if (lv_info(lv, &info) && info.exists && + !arg_count(cmd, force_ARG)) { if (yes_no_prompt("Logical volume %s will be " "deactivated first. " "Continue? [y/n]: ", @@ -301,6 +291,52 @@ static int lvchange_persistent(struct cmd_context *cmd, return 1; } +static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv, + int arg) +{ + const char *tag; + + if (!(tag = arg_str_value(cmd, arg, NULL))) { + log_error("Failed to get tag"); + return 0; + } + + if (!(lv->vg->fid->fmt->features & FMT_TAGS)) { + log_error("Logical volume %s/%s does not support tags", + lv->vg->name, lv->name); + return 0; + } + + if ((arg == addtag_ARG)) { + if (!str_list_add(cmd->mem, &lv->tags, tag)) { + log_error("Failed to add tag %s to %s/%s", + tag, lv->vg->name, lv->name); + return 0; + } + } else { + if (!str_list_del(&lv->tags, tag)) { + log_error("Failed to remove tag %s from %s/%s", + tag, lv->vg->name, lv->name); + return 0; + } + } + + log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); + if (!vg_write(lv->vg)) { + stack; + return 0; + } + + backup(lv->vg); + + /* No need to suspend LV for this change */ + if (!vg_commit(lv->vg)) { + stack; + return 0; + } + + return 1; +} static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { @@ -368,6 +404,22 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv, doit += lvchange_persistent(cmd, lv); } + /* add tag */ + if (arg_count(cmd, addtag_ARG)) { + if (!archived && !archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_tag(cmd, lv, addtag_ARG); + } + + /* del tag */ + if (arg_count(cmd, deltag_ARG)) { + if (!archived && !archive(lv->vg)) + return ECMD_FAILED; + archived = 1; + doit += lvchange_tag(cmd, lv, deltag_ARG); + } + if (doit) log_print("Logical volume \"%s\" changed", lv->name); @@ -384,15 +436,17 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv) if (!arg_count(cmd, available_ARG) && !arg_count(cmd, contiguous_ARG) && !arg_count(cmd, permission_ARG) && !arg_count(cmd, readahead_ARG) && !arg_count(cmd, minor_ARG) && !arg_count(cmd, major_ARG) - && !arg_count(cmd, persistent_ARG)) { - log_error("One or more of -a, -C, -j, -m, -M, -p or -r " - "required"); + && !arg_count(cmd, persistent_ARG) && !arg_count(cmd, addtag_ARG) + && !arg_count(cmd, deltag_ARG)) { + log_error("One or more of -a, -C, -j, -m, -M, -p, -r, " + "--addtag or --deltag required"); return EINVALID_CMD_LINE; } if (arg_count(cmd, ignorelockingfailure_ARG) && (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) || - arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG))) { + arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) || + arg_count(cmd, addtag_ARG) || arg_count(cmd, deltag_ARG))) { log_error("Only -a permitted with --ignorelockingfailure"); return EINVALID_CMD_LINE; } diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 27830973a..65d781d6c 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -361,6 +361,7 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) struct volume_group *vg; struct logical_volume *lv, *org = NULL; struct list *pvh; + const char *tag; int consistent = 1; if (lp->contiguous) @@ -457,9 +458,8 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) status |= LVM_WRITE; } - if (!(lv = lv_create(vg->fid, lp->lv_name, status, alloc, - lp->stripes, lp->stripe_size, lp->extents, - vg, pvh))) + if (!(lv = lv_create(vg->fid, lp->lv_name, status, alloc, lp->stripes, + lp->stripe_size, lp->extents, vg, pvh))) return 0; if (lp->read_ahead) { @@ -475,6 +475,25 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) lv->minor); } + if (arg_count(cmd, addtag_ARG)) { + if (!(tag = arg_str_value(cmd, addtag_ARG, NULL))) { + log_error("Failed to get tag"); + return 0; + } + + if (!(lv->vg->fid->fmt->features & FMT_TAGS)) { + log_error("Volume group %s does not support tags", + lv->vg->name); + return 0; + } + + if (!str_list_add(cmd->mem, &lv->tags, tag)) { + log_error("Failed to add tag %s to %s/%s", + tag, lv->vg->name, lv->name); + return 0; + } + } + if (!archive(vg)) return 0; diff --git a/tools/lvm.c b/tools/lvm.c index 3791decf3..e1c9bfc76 100644 --- a/tools/lvm.c +++ b/tools/lvm.c @@ -243,6 +243,20 @@ int major_arg(struct cmd_context *cmd, struct arg *a) int string_arg(struct cmd_context *cmd, struct arg *a) { + + return 1; +} + +int tag_arg(struct cmd_context *cmd, struct arg *a) +{ + char *pos = a->value; + + if (*pos == '@') + pos++; + + if (!validate_name(pos)) + return 0; + return 1; } diff --git a/tools/pvchange.c b/tools/pvchange.c index 602544ff0..b3e5de147 100644 --- a/tools/pvchange.c +++ b/tools/pvchange.c @@ -31,13 +31,24 @@ static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv, uint64_t sector; const char *pv_name = dev_name(pv->dev); + const char *tag = NULL; int consistent = 1; int allocatable = 0; + int tagarg = 0; + + if (arg_count(cmd, addtag_ARG)) + tagarg = addtag_ARG; + else if (arg_count(cmd, deltag_ARG)) + tagarg = deltag_ARG; if (arg_count(cmd, allocatable_ARG)) allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"), "y"); + else if (tagarg && !(tag = arg_str_value(cmd, tagarg, NULL))) { + log_error("Failed to get tag"); + return 0; + } /* If in a VG, must change using volume group. */ if (*pv->vg_name) { @@ -75,6 +86,12 @@ static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv, pv_name, vg->name); return 0; } + if (tagarg && !(vg->fid->fmt->features & FMT_TAGS)) { + unlock_vg(cmd, pv->vg_name); + log_error("Volume group containing %s does not " + "support tags", pv_name); + return 0; + } if (arg_count(cmd, uuid_ARG) && lvs_in_vg_activated(vg)) { unlock_vg(cmd, pv->vg_name); log_error("Volume group containing %s has active " @@ -85,6 +102,11 @@ static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv, if (!archive(vg)) return 0; } else { + if (tagarg) { + log_error("Can't change tag on Physical Volume %s not " + "in volume group", pv_name); + return 0; + } if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) { log_error("Can't get lock for orphans"); return 0; @@ -137,6 +159,21 @@ static int _pvchange_single(struct cmd_context *cmd, struct physical_volume *pv, "allocatable", pv_name); pv->status &= ~ALLOCATABLE_PV; } + } else if (tagarg) { + /* tag or deltag */ + if ((tagarg == addtag_ARG)) { + if (!str_list_add(cmd->mem, &pv->tags, tag)) { + log_error("Failed to add tag %s to physical " + "volume %s", tag, pv_name); + return 0; + } + } else { + if (!str_list_del(&pv->tags, tag)) { + log_error("Failed to remove tag %s from " + "physical volume" "%s", tag, pv_name); + return 0; + } + } } else { /* --uuid: Change PV ID randomly */ id_create(&pv->id); @@ -182,9 +219,10 @@ int pvchange(struct cmd_context *cmd, int argc, char **argv) list_init(&mdas); - if (arg_count(cmd, allocatable_ARG) + - + arg_count(cmd, uuid_ARG) != 1) { - log_error("Please give exactly one option of -x or --uuid"); + if (arg_count(cmd, allocatable_ARG) + arg_count(cmd, addtag_ARG) + + arg_count(cmd, deltag_ARG) + arg_count(cmd, uuid_ARG) != 1) { + log_error("Please give exactly one option of -x, -uuid, " + "--addtag or --deltag"); return EINVALID_CMD_LINE; } diff --git a/tools/toollib.c b/tools/toollib.c index 730fdc6ec..52fec9d78 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Sistina Software (UK) Limited. + * Copyright (C) 2001-2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ @@ -9,6 +9,7 @@ #include int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + struct list *arg_lvnames, struct list *tags, void *handle, int (*process_single) (struct cmd_context * cmd, struct logical_volume * lv, @@ -16,6 +17,10 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, { int ret_max = 0; int ret = 0; + int process_all = 0; + int process_lv = 0; + int tags_supplied = 0; + int lvargs_supplied = 0; struct lv_list *lvl; @@ -24,14 +29,47 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, return ECMD_FAILED; } + if (tags && !list_empty(tags)) + tags_supplied = 1; + + if (arg_lvnames && !list_empty(arg_lvnames)) + lvargs_supplied = 1; + + /* Process all LVs in this VG if no restrictions given */ + if (!tags_supplied && !lvargs_supplied) + process_all = 1; + + /* Or if VG tags match */ + if (!process_lv && tags_supplied && + str_list_match_list(tags, &vg->tags)) + process_all = 1; + list_iterate_items(lvl, &vg->lvs) { + /* Should we process this LV? */ + if (process_all) + process_lv = 1; + else + process_lv = 0; + + /* LV tag match? */ + if (!process_lv && tags_supplied && + str_list_match_list(tags, &lvl->lv->tags)) + process_lv = 1; + + /* LV name match? */ + if (!process_lv && lvargs_supplied && + str_list_match_item(arg_lvnames, lvl->lv->name)) + process_lv = 1; + + if (!process_lv) + continue; + ret = process_single(cmd, lvl->lv, handle); if (ret > ret_max) ret_max = ret; } return ret_max; - } struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname, @@ -60,26 +98,52 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, int opt = 0; int ret_max = 0; int ret = 0; - int vg_count = 0; int consistent; - struct list *slh, *vgnames; + struct list *slh, *tags_arg; + struct list *vgnames; /* VGs to process */ + struct str_list *sll; struct volume_group *vg; - struct logical_volume *lv; - struct lv_list *lvl; + struct list tags, lvnames; + struct list arg_lvnames; /* Cmdline vgname or vgname/lvname */ + char *vglv; + size_t vglv_sz; const char *vgname; + list_init(&tags); + list_init(&arg_lvnames); + if (argc) { + struct list arg_vgnames; + log_verbose("Using logical volume(s) on command line"); + list_init(&arg_vgnames); + for (; opt < argc; opt++) { - char *lv_name = argv[opt]; + const char *lv_name = argv[opt]; char *vgname_def; - int vgname_provided = 1; int dev_dir_found = 0; - /* Do we have a vgname or lvname? */ + /* Do we have a tag or vgname or lvname? */ vgname = lv_name; + + if (*vgname == '@') { + if (!validate_name(vgname + 1)) { + log_error("Skipping invalid tag %s", + vgname); + continue; + } + if (!str_list_add(cmd->mem, &tags, + pool_strdup(cmd->mem, + vgname + 1))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; + } + continue; + } + + /* FIXME Jumbled parsing */ if (*vgname == '/') { while (*vgname == '/') vgname++; @@ -94,131 +158,124 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, } if (*vgname == '/') { log_error("\"%s\": Invalid path for Logical " - "Volume", lv_name); + "Volume", argv[opt]); if (ret_max < ECMD_FAILED) ret_max = ECMD_FAILED; continue; } + lv_name = vgname; if (strchr(vgname, '/')) { /* Must be an LV */ - vgname_provided = 0; - if (!(vgname = extract_vgname(cmd, lv_name))) { + lv_name = strchr(vgname, '/'); + while (*lv_name == '/') + lv_name++; + if (!(vgname = extract_vgname(cmd, vgname))) { if (ret_max < ECMD_FAILED) ret_max = ECMD_FAILED; continue; } - } - - if (!dev_dir_found && + } else if (!dev_dir_found && (vgname_def = default_vgname(cmd))) { - vgname_provided = 0; vgname = vgname_def; - } + } else + lv_name = NULL; - log_verbose("Finding volume group \"%s\"", vgname); - if (!lock_vol(cmd, vgname, lock_type)) { - log_error("Can't lock %s: skipping", vgname); - continue; - } - if (lock_type & LCK_WRITE) - consistent = 1; - else - consistent = 0; - if (!(vg = vg_read(cmd, vgname, &consistent)) || - !consistent) { - unlock_vg(cmd, vgname); - if (!vg) - log_error("Volume group \"%s\" " - "not found", vgname); - else - log_error("Volume group \"%s\" " - "inconsistent", vgname); - if (!vg || !(vg = - recover_vg(cmd, vgname, - lock_type))) { - unlock_vg(cmd, vgname); - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - continue; - } - } - - if (vg->status & EXPORTED_VG) { - log_error("Volume group \"%s\" is exported", - vg->name); - unlock_vg(cmd, vgname); + if (!str_list_add(cmd->mem, &arg_vgnames, + pool_strdup(cmd->mem, vgname))) { + log_error("strlist allocation failed"); return ECMD_FAILED; } - if (vgname_provided) { - if ((ret = - process_each_lv_in_vg(cmd, vg, handle, - process_single)) > - ret_max) - ret_max = ret; - unlock_vg(cmd, vgname); - continue; - } - - if (!(lvl = find_lv_in_vg(vg, lv_name))) { - log_error("Can't find logical volume \"%s\" " - "in volume group \"%s\"", - lv_name, vgname); - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - unlock_vg(cmd, vgname); - continue; + if (!lv_name) { + if (!str_list_add(cmd->mem, &arg_lvnames, + pool_strdup(cmd->mem, vgname))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; + } + } else { + vglv_sz = strlen(vgname) + strlen(lv_name) + 2; + if (!(vglv = pool_alloc(cmd->mem, vglv_sz)) || + lvm_snprintf(vglv, vglv_sz, "%s/%s", vgname, + lv_name) < 0) { + log_error("vg/lv string alloc failed"); + return ECMD_FAILED; + } + if (!str_list_add(cmd->mem, &arg_lvnames, + vglv)) { + log_error("strlist allocation failed"); + return ECMD_FAILED; + } } - - lv = lvl->lv; - - if ((ret = process_single(cmd, lv, handle)) > ret_max) - ret_max = ret; - unlock_vg(cmd, vgname); } - } else { + vgnames = &arg_vgnames; + } + + if (!argc || !list_empty(&tags)) { log_verbose("Finding all logical volumes"); - if (!(vgnames = get_vgs(cmd, 0))) { + if (!(vgnames = get_vgs(cmd, 0)) || list_empty(vgnames)) { log_error("No volume groups found"); return ECMD_FAILED; } - list_iterate(slh, vgnames) { - vgname = list_item(slh, struct str_list)->str; - if (!vgname || !*vgname) - continue; /* FIXME Unnecessary? */ - if (!lock_vol(cmd, vgname, lock_type)) { - log_error("Can't lock %s: skipping", vgname); - continue; - } - if (lock_type & LCK_WRITE) - consistent = 1; + } + + list_iterate(slh, vgnames) { + vgname = list_item(slh, struct str_list)->str; + if (!vgname || !*vgname) + continue; /* FIXME Unnecessary? */ + if (!lock_vol(cmd, vgname, lock_type)) { + log_error("Can't lock %s: skipping", vgname); + continue; + } + if (lock_type & LCK_WRITE) + consistent = 1; + else + consistent = 0; + if (!(vg = vg_read(cmd, vgname, &consistent)) || + !consistent) { + unlock_vg(cmd, vgname); + if (!vg) + log_error("Volume group \"%s\" " + "not found", vgname); else - consistent = 0; - if (!(vg = vg_read(cmd, vgname, &consistent)) || - !consistent) { + log_error("Volume group \"%s\" " + "inconsistent", vgname); + if (!vg || !(vg = + recover_vg(cmd, vgname, + lock_type))) { unlock_vg(cmd, vgname); - if (!vg) - log_error("Volume group \"%s\" " - "not found", vgname); - else - log_error("Volume group \"%s\" " - "inconsistent", vgname); - if (!vg || !(vg = - recover_vg(cmd, vgname, - lock_type))) { - unlock_vg(cmd, vgname); - if (ret_max < ECMD_FAILED) - ret_max = ECMD_FAILED; - continue; + if (ret_max < ECMD_FAILED) + ret_max = ECMD_FAILED; + continue; + } + } + + tags_arg = &tags; + list_init(&lvnames); /* LVs to be processed in this VG */ + list_iterate_items(sll, &arg_lvnames) { + const char *vg_name = sll->str; + const char *lv_name = strchr(vg_name, '/'); + + if ((!lv_name && !strcmp(vg_name, vgname))) { + /* Process all LVs in this VG */ + tags_arg = NULL; + list_init(&lvnames); + break; + } else if (!strncmp(vg_name, vgname, strlen(vgname)) && + strlen(vgname) == lv_name - vg_name) { + if (!str_list_add(cmd->mem, &lvnames, + pool_strdup(cmd->mem, + lv_name + 1))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; } } - ret = process_each_lv_in_vg(cmd, vg, handle, - process_single); - unlock_vg(cmd, vgname); - if (ret > ret_max) - ret_max = ret; - vg_count++; } + + ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, + handle, process_single); + unlock_vg(cmd, vgname); + if (ret > ret_max) + ret_max = ret; } return ret_max; @@ -244,6 +301,44 @@ int process_each_segment_in_lv(struct cmd_context *cmd, return ret_max; } +static int _process_one_vg(struct cmd_context *cmd, const char *vg_name, + struct list *tags, struct list *arg_vgnames, + int lock_type, int consistent, void *handle, + int ret_max, + int (*process_single) (struct cmd_context * cmd, + const char *vg_name, + struct volume_group * vg, + int consistent, void *handle)) +{ + struct volume_group *vg; + int ret = 0; + + if (!lock_vol(cmd, vg_name, lock_type)) { + log_error("Can't lock %s: skipping", vg_name); + return ret_max; + } + + log_verbose("Finding volume group \"%s\"", vg_name); + vg = vg_read(cmd, vg_name, &consistent); + + if (!list_empty(tags)) { + /* Only process if a tag matches or it's on arg_vgnames */ + if (!str_list_match_item(arg_vgnames, vg_name) && + !str_list_match_list(tags, &vg->tags)) { + unlock_vg(cmd, vg_name); + return ret_max; + } + } + + if ((ret = process_single(cmd, vg_name, vg, consistent, + handle)) > ret_max) + ret_max = ret; + + unlock_vg(cmd, vg_name); + + return ret_max; +} + int process_each_vg(struct cmd_context *cmd, int argc, char **argv, int lock_type, int consistent, void *handle, int (*process_single) (struct cmd_context * cmd, @@ -253,18 +348,37 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, { int opt = 0; int ret_max = 0; - int ret = 0; - struct list *slh, *vgnames; - struct volume_group *vg; + struct str_list *sl; + struct list *vgnames; + struct list arg_vgnames, tags; const char *vg_name; char *dev_dir = cmd->dev_dir; + list_init(&tags); + list_init(&arg_vgnames); + if (argc) { log_verbose("Using volume group(s) on command line"); + for (; opt < argc; opt++) { vg_name = argv[opt]; + if (*vg_name == '@') { + if (!validate_name(vg_name + 1)) { + log_error("Skipping invalid tag %s", + vg_name); + continue; + } + if (!str_list_add(cmd->mem, &tags, + pool_strdup(cmd->mem, + vg_name + 1))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; + } + continue; + } + if (*vg_name == '/') { while (*vg_name == '/') vg_name++; @@ -277,46 +391,38 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, vg_name); continue; } - if (!lock_vol(cmd, vg_name, lock_type)) { - log_error("Can't lock %s: skipping", vg_name); - continue; + if (!str_list_add(cmd->mem, &arg_vgnames, + pool_strdup(cmd->mem, vg_name))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; } - log_verbose("Finding volume group \"%s\"", vg_name); - vg = vg_read(cmd, vg_name, &consistent); - if ((ret = process_single(cmd, vg_name, vg, consistent, - handle)) > ret_max) - ret_max = ret; - unlock_vg(cmd, vg_name); } - } else { + + vgnames = &arg_vgnames; + } + + if (!argc || !list_empty(&tags)) { log_verbose("Finding all volume groups"); if (!(vgnames = get_vgs(cmd, 0)) || list_empty(vgnames)) { log_error("No volume groups found"); return ECMD_FAILED; } - list_iterate(slh, vgnames) { - vg_name = list_item(slh, struct str_list)->str; - if (!vg_name || !*vg_name) - continue; /* FIXME Unnecessary? */ - if (!lock_vol(cmd, vg_name, lock_type)) { - log_error("Can't lock %s: skipping", vg_name); - continue; - } - log_verbose("Finding volume group \"%s\"", vg_name); - vg = vg_read(cmd, vg_name, &consistent); - ret = process_single(cmd, vg_name, vg, consistent, - handle); - if (ret > ret_max) - ret_max = ret; - unlock_vg(cmd, vg_name); - } + } + + list_iterate_items(sl, vgnames) { + vg_name = sl->str; + if (!vg_name || !*vg_name) + continue; /* FIXME Unnecessary? */ + ret_max = _process_one_vg(cmd, vg_name, &tags, &arg_vgnames, + lock_type, consistent, handle, + ret_max, process_single); } return ret_max; } int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, - void *handle, + struct list *tags, void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, struct physical_volume * pv, @@ -326,9 +432,13 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, int ret = 0; struct pv_list *pvl; - list_iterate_items(pvl, &vg->pvs) + list_iterate_items(pvl, &vg->pvs) { + if (tags && !list_empty(tags) && + !str_list_match_list(tags, &pvl->pv->tags)) + continue; if ((ret = process_single(cmd, vg, pvl->pv, handle)) > ret_max) ret_max = ret; + } return ret_max; } @@ -346,11 +456,33 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, struct pv_list *pvl; struct physical_volume *pv; - struct list *pvslist; + struct list *pvslist, *vgnames; + struct list tags; + struct str_list *sll; + char *tagname; + int consistent = 1; + + list_init(&tags); if (argc) { log_verbose("Using physical volume(s) on command line"); for (; opt < argc; opt++) { + if (*argv[opt] == '@') { + tagname = argv[opt] + 1; + + if (!validate_name(tagname)) { + log_error("Skipping invalid tag %s", + tagname); + continue; + } + if (!str_list_add(cmd->mem, &tags, + pool_strdup(cmd->mem, + tagname))) { + log_error("strlist allocation failed"); + return ECMD_FAILED; + } + continue; + } if (vg) { if (!(pvl = find_pv_in_vg(vg, argv[opt]))) { log_error("Physical Volume \"%s\" not " @@ -374,11 +506,25 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, if (ret > ret_max) ret_max = ret; } + if (!list_empty(&tags) && (vgnames = get_vgs(cmd, 0)) && + !list_empty(vgnames)) { + list_iterate_items(sll, vgnames) { + vg = vg_read(cmd, sll->str, &consistent); + if (!consistent) + continue; + ret = process_each_pv_in_vg(cmd, vg, &tags, + handle, + process_single); + if (ret > ret_max) + ret_max = ret; + } + } } else { if (vg) { log_verbose("Using all physical volume(s) in " "volume group"); - ret = process_each_pv_in_vg(cmd, vg, handle, process_single); + ret = process_each_pv_in_vg(cmd, vg, NULL, handle, + process_single); if (ret > ret_max) ret_max = ret; } else { @@ -424,7 +570,7 @@ const char *extract_vgname(struct cmd_context *cmd, const char *lv_name) "Volume", lv_name); return 0; } - + /* Require exactly one set of consecutive slashes */ if ((st = strchr(vg_name, '/'))) while (*st == '/') @@ -589,13 +735,56 @@ static int _parse_pes(struct pool *mem, char *c, struct list *alloc_areas, return 0; } +static void _create_pv_entry(struct pool *mem, struct pv_list *pvl, + char *colon, struct list *r) +{ + const char *pvname; + struct pv_list *new_pvl; + struct list *alloc_areas; + + pvname = dev_name(pvl->pv->dev); + if (!(pvl->pv->status & ALLOCATABLE_PV)) { + log_error("Physical volume %s not allocatable", pvname); + return; + } + + if (pvl->pv->pe_count == pvl->pv->pe_alloc_count) { + log_err("No free extents on physical volume \"%s\"", + pvname); + return; + } + + if (!(new_pvl = pool_alloc(mem, sizeof(*new_pvl)))) { + log_err("Unable to allocate physical volume list."); + return; + } + + memcpy(new_pvl, pvl, sizeof(*new_pvl)); + + if (!(alloc_areas = pool_alloc(mem, sizeof(*alloc_areas)))) { + log_error("Allocation of alloc_areas list failed"); + return; + } + list_init(alloc_areas); + + /* Specify which physical extents may be used for allocation */ + if (!_parse_pes(mem, colon, alloc_areas, pvl->pv->pe_count)) { + stack; + return; + } + new_pvl->alloc_areas = alloc_areas; + + list_add(r, &new_pvl->list); +} + struct list *create_pv_list(struct pool *mem, struct volume_group *vg, int argc, char **argv) { struct list *r; - struct list *alloc_areas; - struct pv_list *pvl, *new_pvl; - char *pvname = NULL, *colon; + struct pv_list *pvl; + struct list tags, arg_pvnames; + const char *pvname = NULL; + char *colon, *tagname; int i; /* Build up list of PVs */ @@ -605,59 +794,53 @@ struct list *create_pv_list(struct pool *mem, } list_init(r); + list_init(&tags); + list_init(&arg_pvnames); + for (i = 0; i < argc; i++) { - if ((colon = strchr(argv[i], ':'))) { - if (!(pvname = pool_strndup(mem, argv[i], - (unsigned) (colon - - argv[i])))) { - log_error("Failed to clone PV name"); - return NULL; + if (*argv[i] == '@') { + tagname = argv[i] + 1; + if (!validate_name(tagname)) { + log_error("Skipping invalid tag %s", tagname); + continue; + } + list_iterate_items(pvl, &vg->pvs) { + if (str_list_match_item(&pvl->pv->tags, + tagname)) { + _create_pv_entry(mem, pvl, NULL, r); + } } - } else - pvname = argv[i]; - - if (!(pvl = find_pv_in_vg(vg, pvname))) { - log_err("Physical Volume \"%s\" not found in " - "Volume Group \"%s\"", pvname, vg->name); - return NULL; - } - - if (!(pvl->pv->status & ALLOCATABLE_PV)) { - log_error("Physical volume %s not allocatable", pvname); - continue; - } - - if (pvl->pv->pe_count == pvl->pv->pe_alloc_count) { - log_err("No free extents on physical volume \"%s\"", - pvname); continue; } - if (!(new_pvl = pool_alloc(mem, sizeof(*new_pvl)))) { - log_err("Unable to allocate physical volume list."); - return NULL; - } - - memcpy(new_pvl, pvl, sizeof(*new_pvl)); - list_add(r, &new_pvl->list); + pvname = argv[i]; - if (!(alloc_areas = pool_alloc(mem, sizeof(*alloc_areas)))) { - log_error("Allocation of alloc_areas list failed"); - return NULL; + if ((colon = strchr(pvname, ':'))) { + if (!(pvname = pool_strndup(mem, pvname, + (unsigned) (colon - + pvname)))) { + log_error("Failed to clone PV name"); + return NULL; + } } - list_init(alloc_areas); - /* Specify which physical extents may be used for allocation */ - if (!_parse_pes(mem, colon, alloc_areas, pvl->pv->pe_count)) { - stack; - return NULL; - } - new_pvl->alloc_areas = alloc_areas; + if (!(pvl = find_pv_in_vg(vg, pvname))) { + log_err("Physical Volume \"%s\" not found in " + "Volume Group \"%s\"", pvname, vg->name); + return NULL; + } + _create_pv_entry(mem, pvl, colon, r); } + if (list_empty(r)) + log_error("No specified PVs have space available"); + return list_empty(r) ? NULL : r; } + + + struct list *clone_pv_list(struct pool *mem, struct list *pvsl) { struct list *r; diff --git a/tools/toollib.h b/tools/toollib.h index 1a7e7d11a..c87088424 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -59,13 +59,14 @@ int process_each_segment_in_lv(struct cmd_context *cmd, void *handle)); int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, - void *handle, + struct list *tags, void *handle, int (*process_single) (struct cmd_context * cmd, struct volume_group * vg, struct physical_volume * pv, void *handle)); int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + struct list *arg_lvnames, struct list *tags, void *handle, int (*process_single) (struct cmd_context * cmd, struct logical_volume * lv, diff --git a/tools/tools.h b/tools/tools.h index d3d9cbb8d..e6f69374e 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -33,6 +33,7 @@ #include "lvm-file.h" #include "lvm-string.h" #include "pool.h" +#include "str_list.h" #include "toolcontext.h" #include "toollib.h" @@ -104,6 +105,7 @@ int int_arg_with_sign(struct cmd_context *cmd, struct arg *a); int major_arg(struct cmd_context *cmd, struct arg *a); int minor_arg(struct cmd_context *cmd, struct arg *a); int string_arg(struct cmd_context *cmd, struct arg *a); +int tag_arg(struct cmd_context *cmd, struct arg *a); int permission_arg(struct cmd_context *cmd, struct arg *a); int metadatatype_arg(struct cmd_context *cmd, struct arg *a); int units_arg(struct cmd_context *cmd, struct arg *a); diff --git a/tools/vgchange.c b/tools/vgchange.c index 1224a849c..21029293d 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -163,6 +163,48 @@ static int _vgchange_logicalvolume(struct cmd_context *cmd, return ECMD_PROCESSED; } +static int _vgchange_tag(struct cmd_context *cmd, struct volume_group *vg, + int arg) +{ + const char *tag; + + if (!(tag = arg_str_value(cmd, arg, NULL))) { + log_error("Failed to get tag"); + return ECMD_FAILED; + } + + if (!(vg->fid->fmt->features & FMT_TAGS)) { + log_error("Volume group %s does not support tags", vg->name); + return ECMD_FAILED; + } + + if (!archive(vg)) + return ECMD_FAILED; + + if ((arg == addtag_ARG)) { + if (!str_list_add(cmd->mem, &vg->tags, tag)) { + log_error("Failed to add tag %s to volume group %s", + tag, vg->name); + return ECMD_FAILED; + } + } else { + if (!str_list_del(&vg->tags, tag)) { + log_error("Failed to remove tag %s from volume group " + "%s", tag, vg->name); + return ECMD_FAILED; + } + } + + if (!vg_write(vg) || !vg_commit(vg)) + return ECMD_FAILED; + + backup(vg); + + log_print("Volume group \"%s\" successfully changed", vg->name); + + return ECMD_PROCESSED; +} + static int _vgchange_uuid(struct cmd_context *cmd, struct volume_group *vg) { struct lv_list *lvl; @@ -195,7 +237,7 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, int consistent, void *handle) { - int r = 0; + int r = ECMD_FAILED; if (!vg) { log_error("Unable to find volume group \"%s\"", vg_name); @@ -229,6 +271,12 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name, else if (arg_count(cmd, logicalvolume_ARG)) r = _vgchange_logicalvolume(cmd, vg); + else if (arg_count(cmd, addtag_ARG)) + r = _vgchange_tag(cmd, vg, addtag_ARG); + + else if (arg_count(cmd, deltag_ARG)) + r = _vgchange_tag(cmd, vg, deltag_ARG); + else if (arg_count(cmd, uuid_ARG)) r = _vgchange_uuid(cmd, vg); @@ -239,14 +287,18 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) { if (! (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + - arg_count(cmd, resizeable_ARG) + arg_count(cmd, uuid_ARG))) { - log_error("One of -a, -l, --uuid or -x options required"); + arg_count(cmd, resizeable_ARG) + arg_count(cmd, deltag_ARG) + + arg_count(cmd, addtag_ARG) + arg_count(cmd, uuid_ARG))) { + log_error("One of -a, -l, -x, --addtag, --deltag or --uuid " + "options required"); return EINVALID_CMD_LINE; } if (arg_count(cmd, available_ARG) + arg_count(cmd, logicalvolume_ARG) + - arg_count(cmd, resizeable_ARG) + arg_count(cmd, uuid_ARG) > 1) { - log_error("Only one of -a, -l, --uuid or -x options allowed"); + arg_count(cmd, resizeable_ARG) + arg_count(cmd, deltag_ARG) + + arg_count(cmd, addtag_ARG) + arg_count(cmd, uuid_ARG) > 1) { + log_error("Only one of -a, -l, -x, --addtag, --deltag or --uuid" + " options allowed"); return EINVALID_CMD_LINE; } diff --git a/tools/vgcreate.c b/tools/vgcreate.c index 0530b1e37..e6a9b04f7 100644 --- a/tools/vgcreate.c +++ b/tools/vgcreate.c @@ -29,6 +29,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) char *vg_name; char vg_path[PATH_MAX]; struct volume_group *vg; + const char *tag; if (!argc) { log_error("Please provide volume group name and " @@ -108,6 +109,24 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv) log_error("Warning: Setting maxphysicalvolumes to %d " "(0 means unlimited)", vg->max_pv); + if (arg_count(cmd, addtag_ARG)) { + if (!(tag = arg_str_value(cmd, addtag_ARG, NULL))) { + log_error("Failed to get tag"); + return ECMD_FAILED; + } + + if (!(vg->fid->fmt->features & FMT_TAGS)) { + log_error("Volume group format does not support tags"); + return ECMD_FAILED; + } + + if (!str_list_add(cmd->mem, &vg->tags, tag)) { + log_error("Failed to add tag %s to volume group %s", + tag, vg_name); + return ECMD_FAILED; + } + } + if (!lock_vol(cmd, "", LCK_VG_WRITE)) { log_error("Can't get lock for orphan PVs"); return ECMD_FAILED; diff --git a/tools/vgdisplay.c b/tools/vgdisplay.c index f777f73eb..1409d7b95 100644 --- a/tools/vgdisplay.c +++ b/tools/vgdisplay.c @@ -51,10 +51,11 @@ static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name, if (arg_count(cmd, verbose_ARG)) { vgdisplay_extents(vg); - process_each_lv_in_vg(cmd, vg, NULL, &lvdisplay_full); + process_each_lv_in_vg(cmd, vg, NULL, NULL, NULL, + &lvdisplay_full); log_print("--- Physical volumes ---"); - process_each_pv_in_vg(cmd, vg, NULL, &pvdisplay_short); + process_each_pv_in_vg(cmd, vg, NULL, NULL, &pvdisplay_short); } return ECMD_PROCESSED; -- 2.43.5