]> sourceware.org Git - lvm2.git/commitdiff
tagging
authorAlasdair Kergon <agk@redhat.com>
Mon, 8 Mar 2004 17:19:15 +0000 (17:19 +0000)
committerAlasdair Kergon <agk@redhat.com>
Mon, 8 Mar 2004 17:19:15 +0000 (17:19 +0000)
26 files changed:
lib/Makefile.in
lib/format1/format1.c
lib/format1/import-export.c
lib/format_text/export.c
lib/format_text/format-text.c
lib/format_text/import-export.h
lib/format_text/import_vsn1.c
lib/format_text/tags.c [new file with mode: 0644]
lib/metadata/lv_manip.c
lib/metadata/merge.c
lib/metadata/metadata.c
lib/metadata/metadata.h
lib/report/columns.h
lib/report/report.c
tools/args.h
tools/commands.h
tools/lvchange.c
tools/lvcreate.c
tools/lvm.c
tools/pvchange.c
tools/toollib.c
tools/toollib.h
tools/tools.h
tools/vgchange.c
tools/vgcreate.c
tools/vgdisplay.c

index cbe6c96acd4f34c9f1dd4f353d3006ecc98c1553..dfa73bf361a33940896ec4e6907a536f10b60fd4 100644 (file)
@@ -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 \
index ec51c3ec6125d0c750c3d1aaa6509c7802029a20..7f3c4e5a3b990ab1fcf2f628007aa694f588e447 100644 (file)
@@ -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;
index bc67c81692fc45a335adda2af443192f8709bc9a..0bbb963db700c8ab1e9445109851acc8212462b4 100644 (file)
@@ -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;
 }
index 0396eaa0f394ea55ce0e6347028504d395aab425..9552dd9134cd38924d40b02d8dff261fbc87686c 100644 (file)
@@ -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));
index a652d8a235b82fa9f839471b9e45bb232668d6d0..0d5b8548c9a0a526e70adefe246f540f42c49e8f 100644 (file)
@@ -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");
index 87ec5c7719b254a6359c9bbb480bcc454e59cda4..54ee4b821c7ce8e128416b942b6931327cd550a3 100644 (file)
@@ -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);
index 0b3311e8cfa7c53a4a9d77414dbc81b17b57eaa6..008026dc79d636c9b828af4a337ceaab9992e104 100644 (file)
@@ -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 (file)
index 0000000..88f96d7
--- /dev/null
@@ -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;
+}
index e72abae12e4a73dc8706227a02bb0b29d99f2248..968562adc735d36d7e3adbaf0f9fa79ee76a73f9 100644 (file)
@@ -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;
index 286a92b8c7f81fe431114daa672f484f43f7b3c7..ed628dca1a4200c07665eeca4c3d1a4e07bdeceb 100644 (file)
@@ -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;
index bf92ec13983e5fdc58bf2c2e943c297db042a972..c165271358d4007ebbc5234689ceecc96ae85cea 100644 (file)
@@ -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'",
index 3d476e78b39c7892e431b3ffcf83d15af6688b0e..e2f633229306da4a93a931135a47ae0e79c87aae 100644 (file)
@@ -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 {
index 2526fcd7053fc75612dbaffd9e4f142a10f14436..8613eea90ff40f475e38ab91ef7e5913b10ba32e 100644 (file)
@@ -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* */
index 97adeb9009becae5545698ede0da78468301803a..054b3a486cd94a69c933f38027d9b377c90a4df6 100644 (file)
@@ -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)
 {
index 2297c5508b8c52294c15152be775ab7e2e71c3e2..4c3da4a78f41c479ff81a964228c9253500ca3da 100644 (file)
@@ -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)
 
index 94b76be52b8566386a49265e79b9a2d0c67abf3c..af8869c978515ca98063fac1a054fd14287f04b5 100644 (file)
@@ -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",
index f20f1df2a17eaa80b0103bbf9d6dca84ab41c9ea..75cfa2195521b064b5529fcc7fdec8dc986ac38c 100644 (file)
@@ -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;
        }
index 27830973a09688a4efef58e827b2be1c0cac1ac8..65d781d6c059ba0ab6a36d7ce614c7d36e61078d 100644 (file)
@@ -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;
 
index 3791decf38985888fb2856d6bf3ab3a19da1cef1..e1c9bfc76f70b400701c8154b27820cb862b813a 100644 (file)
@@ -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;
 }
 
index 602544ff00f4e78c0655942ebb1bee72f65eafa3..b3e5de1477539aa6a10e2e340d8d3a788a972b51 100644 (file)
@@ -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;
        }
 
index 730fdc6ec7cfd96db8aa650456fd4f85630d3f80..52fec9d78e71a0b9b41776a4385b7f7108e979c5 100644 (file)
@@ -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 <sys/stat.h>
 
 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;
index 1a7e7d11af558bd3b23a08cf3e81dbcc5cc67d26..c87088424443794976ee242d8c634ecf0674ca96 100644 (file)
@@ -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,
index d3d9cbb8de147897bac52dd5ed13cd26a1e2d906..e6f69374eaea6d1d3a41fe4b87c301a055f55f51 100644 (file)
@@ -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);
index 1224a849c47a19be1a96f59007a938147af8751c..21029293d68974224f36c2ed7d2863bb536680d7 100644 (file)
@@ -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;
        }
 
index 0530b1e37b60667e8ea63071e0f3ed35c1b33910..e6a9b04f7b75bc3958a2651a225965f35929e7b7 100644 (file)
@@ -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;
index f777f73eb5bc45c0e38a82a550131907f00c7078..1409d7b95595ebfac11a0af9302b51496a22b078 100644 (file)
@@ -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;
This page took 0.096233 seconds and 5 git commands to generate.