master - post-release
by Alasdair Kergon
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=b4e8de3a3156e2...
Commit: b4e8de3a3156e2385e08bf12fb483eb3cad519e2
Parent: 36ce97c6250a513c73e388261ccb376cd351e11f
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Fri Jul 3 16:58:24 2015 +0100
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Fri Jul 3 16:58:24 2015 +0100
post-release
---
VERSION | 2 +-
VERSION_DM | 2 +-
WHATS_NEW | 3 +++
WHATS_NEW_DM | 3 +++
4 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/VERSION b/VERSION
index 48674ea..c32ce9f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.02.124(2)-git (2015-07-03)
+2.02.125(2)-git (2015-07-03)
diff --git a/VERSION_DM b/VERSION_DM
index 61632f7..53b1ce1 100644
--- a/VERSION_DM
+++ b/VERSION_DM
@@ -1 +1 @@
-1.02.101-git (2015-07-03)
+1.02.102-git (2015-07-03)
diff --git a/WHATS_NEW b/WHATS_NEW
index cec1980..f22f0eb 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,3 +1,6 @@
+Version 2.02.125 -
+================================
+
Version 2.02.124 - 3rd July 2015
================================
Move sending thin pool messages from resume to suspend phase.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 0e3b648..8b4faff 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,3 +1,6 @@
+Version 1.02.102 -
+================================
+
Version 1.02.101 - 3rd July 2015
================================
Add experimental support to passing messages in suspend tree.
8 years, 9 months
master - pre-release
by Alasdair Kergon
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=36ce97c6250a51...
Commit: 36ce97c6250a513c73e388261ccb376cd351e11f
Parent: 3dbb9a57ca684271dbbc26300aa2e35da9608aa3
Author: Alasdair G Kergon <agk(a)redhat.com>
AuthorDate: Fri Jul 3 16:34:40 2015 +0100
Committer: Alasdair G Kergon <agk(a)redhat.com>
CommitterDate: Fri Jul 3 16:34:40 2015 +0100
pre-release
---
VERSION | 2 +-
VERSION_DM | 2 +-
WHATS_NEW | 5 +++--
WHATS_NEW_DM | 4 ++--
daemons/lvmlockd/lvmlockctl.c | 11 ++++++++++-
daemons/lvmlockd/lvmlockd-client.h | 4 ++--
daemons/lvmlockd/lvmlockd-core.c | 2 +-
daemons/lvmlockd/lvmlockd-dlm.c | 2 +-
daemons/lvmlockd/lvmlockd-internal.h | 4 ++--
daemons/lvmlockd/lvmlockd-sanlock.c | 3 +--
lib/locking/lvmlockd.c | 3 +--
lib/locking/lvmlockd.h | 7 +++----
12 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/VERSION b/VERSION
index a75fbd2..48674ea 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.02.124(2)-git (2015-06-30)
+2.02.124(2)-git (2015-07-03)
diff --git a/VERSION_DM b/VERSION_DM
index 5c2fe02..61632f7 100644
--- a/VERSION_DM
+++ b/VERSION_DM
@@ -1 +1 @@
-1.02.101-git (2015-06-30)
+1.02.101-git (2015-07-03)
diff --git a/WHATS_NEW b/WHATS_NEW
index 382f290..cec1980 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,8 +1,9 @@
-Version 2.02.124 -
-=================================
+Version 2.02.124 - 3rd July 2015
+================================
Move sending thin pool messages from resume to suspend phase.
Report warning when pool is overprovisioned and not auto resized.
Recognize free-form date/time values for lv_time field in selection criteria.
+ Added experimental lvmlockd with configure --enable-lvmlockd.
Fix regression in select to match string fields if using synonyms (2.02.123).
Fix regression when printing more lv names via display_lvname (2.02.122).
Add missing error logging to unlock_vg and sync_local_dev_names callers.
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index f4537d9..0e3b648 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,5 @@
-Version 1.02.101 -
-=================================
+Version 1.02.101 - 3rd July 2015
+================================
Add experimental support to passing messages in suspend tree.
Add dm_report_value_cache_{set,get} to support caching during report/select.
Add dm_report_reserved_handler to handle report reserved value actions.
diff --git a/daemons/lvmlockd/lvmlockctl.c b/daemons/lvmlockd/lvmlockctl.c
index b8ab9ed..9349ced 100644
--- a/daemons/lvmlockd/lvmlockctl.c
+++ b/daemons/lvmlockd/lvmlockctl.c
@@ -1,3 +1,13 @@
+/*
+ * Copyright (C) 2014-2015 Red Hat, Inc.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ */
+
#define _GNU_SOURCE
#include "configure.h"
#include "lvmlockd-client.h"
@@ -632,4 +642,3 @@ out:
lvmlockd_close(_lvmlockd);
return rv;
}
-
diff --git a/daemons/lvmlockd/lvmlockd-client.h b/daemons/lvmlockd/lvmlockd-client.h
index 0a3e4b2..e1d69d2 100644
--- a/daemons/lvmlockd/lvmlockd-client.h
+++ b/daemons/lvmlockd/lvmlockd-client.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
@@ -46,4 +46,4 @@ static inline void lvmlockd_close(daemon_handle h)
#define EPREPARE 215
#define ELOCKD 216
-#endif
+#endif /* _LVM_LVMLOCKD_CLIENT_H */
diff --git a/daemons/lvmlockd/lvmlockd-core.c b/daemons/lvmlockd/lvmlockd-core.c
index d058ea1..87daaaf 100644
--- a/daemons/lvmlockd/lvmlockd-core.c
+++ b/daemons/lvmlockd/lvmlockd-core.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
diff --git a/daemons/lvmlockd/lvmlockd-dlm.c b/daemons/lvmlockd/lvmlockd-dlm.c
index 5542968..4b4f433 100644
--- a/daemons/lvmlockd/lvmlockd-dlm.c
+++ b/daemons/lvmlockd/lvmlockd-dlm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
diff --git a/daemons/lvmlockd/lvmlockd-internal.h b/daemons/lvmlockd/lvmlockd-internal.h
index 54d0a05..f1aaa7e 100644
--- a/daemons/lvmlockd/lvmlockd-internal.h
+++ b/daemons/lvmlockd/lvmlockd-internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
@@ -370,4 +370,4 @@ void log_level(int level, const char *fmt, ...) __attribute__((format(printf, 2
#define log_error(fmt, args...) log_level(LOG_ERR, fmt, ##args)
#define log_warn(fmt, args...) log_level(LOG_WARNING, fmt, ##args)
-#endif
+#endif /* _LVM_LVMLOCKD_INTERNAL_H */
diff --git a/daemons/lvmlockd/lvmlockd-sanlock.c b/daemons/lvmlockd/lvmlockd-sanlock.c
index 8f3ecff..5a31edd 100644
--- a/daemons/lvmlockd/lvmlockd-sanlock.c
+++ b/daemons/lvmlockd/lvmlockd-sanlock.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
@@ -1713,4 +1713,3 @@ int lm_is_running_sanlock(void)
return 0;
return 1;
}
-
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index 55a64a1..5d0cc07 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
@@ -2585,4 +2585,3 @@ int lockd_lv_uses_lock(struct logical_volume *lv)
return 0;
return 1;
}
-
diff --git a/lib/locking/lvmlockd.h b/lib/locking/lvmlockd.h
index 17c7222..2bd2fcc 100644
--- a/lib/locking/lvmlockd.h
+++ b/lib/locking/lvmlockd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014-2015 Red Hat, Inc.
*
* This file is part of LVM2.
*
@@ -233,7 +233,6 @@ static inline int lockd_lv_uses_lock(struct logical_volume *lv)
return 0;
}
-#endif /* LVMLOCKD_SUPPORT */
-
-#endif
+#endif /* LVMLOCKD_SUPPORT */
+#endif /* _LVMLOCKD_H */
8 years, 9 months
master - tests: update for new thin pool messaging
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=3dbb9a57ca6842...
Commit: 3dbb9a57ca684271dbbc26300aa2e35da9608aa3
Parent: a900d150e4658a5d72c39acdd4fefd069b8f00b8
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jul 1 13:31:23 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jul 3 16:13:15 2015 +0200
tests: update for new thin pool messaging
---
test/shell/lvextend-thin-metadata-dmeventd.sh | 2 +-
test/shell/thin-volume-list.sh | 7 ++-----
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/test/shell/lvextend-thin-metadata-dmeventd.sh b/test/shell/lvextend-thin-metadata-dmeventd.sh
index 473f1ca..72cdb81 100644
--- a/test/shell/lvextend-thin-metadata-dmeventd.sh
+++ b/test/shell/lvextend-thin-metadata-dmeventd.sh
@@ -76,7 +76,7 @@ grep expected out
check inactive $vg pool_tmeta
# Transaction_id is higher by 1
-fake_metadata_ 10 2 >data
+fake_metadata_ 10 3 >data
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
not vgchange -ay $vg 2>&1 | tee out
diff --git a/test/shell/thin-volume-list.sh b/test/shell/thin-volume-list.sh
index c3dab25..88cae35 100644
--- a/test/shell/thin-volume-list.sh
+++ b/test/shell/thin-volume-list.sh
@@ -29,11 +29,8 @@ lvcreate -T -L8M $vg/pool -V10M -n $lv1
# skip $vg from activation
aux lvmconf "activation/volume_list = [ \"$vg1\" ]"
-# We still could pass - since pool is still active
-lvcreate -V10 -n $lv2 -T $vg/pool
-
-# but $lv2 is not active
-check inactive $vg $lv2
+# We cannot pass - pool volume cannot be manipulated
+not lvcreate -V10 -n $lv2 -T $vg/pool
vgchange -an $vg
8 years, 9 months
master - thin: move pool messaging from resume to suspend
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=a900d150e4658a...
Commit: a900d150e4658a5d72c39acdd4fefd069b8f00b8
Parent: 5bef18f2eb1071a29b682f6f456a13a73493868e
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jul 1 13:31:37 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jul 3 16:13:14 2015 +0200
thin: move pool messaging from resume to suspend
Existing messaging intarface for thin-pool has a few 'weak' points:
* Message were posted with each 'resume' operation, thus not allowing
activation of thin-pool with the existing state.
* Acceleration skipped suspend step has not worked in cluster,
since clvmd resumes only nodes which are suspended (have proper lock
state).
* Resume may fail and code is not really designed to 'fail' in this
phase (generic rule here is resume DOES NOT fail unless something serious
is wrong and lvm2 tool usually doesn't handle recovery path in this case.)
* Full thin-pool suspend happened, when taken a thin-volume snapshot.
With this patch the new method relocates message passing into suspend
state.
This has a few drawbacks with current API, but overal it performs
better and gives are more posibilities to deal with errors.
Patch introduces a new logic for 'origin-only' suspend of thin-pool and
this also relates to thin-volume when taking snapshot.
When suspend_origin_only operation is invoked on a pool with
queued messages then only those messages are posted to thin-pool and
actual suspend of thin pool and data and metadata volume is skipped.
This makes taking a snapshot of thin-volume lighter operation and
avoids blocking of other unrelated active thin volumes.
Also fail now happens in 'suspend' state where the 'Fail' is more expected
and it is better handled through error paths.
Activation of thin-pool is now not sending any message and leaves upto a tool
to decided later how to finish unfinished double-commit transaction.
Problem which needs some API improvements relates to the lvm2 tree
construction. For the suspend tree we do not add target table line
into the tree, but only a device is inserted into a tree.
Current mechanism to attach messages for thin-pool requires the libdm
to know about thin-pool target, so lvm2 currently takes assumption, node
is really a thin-pool and fills in the table line for this node (which
should be ensured by the PRELOAD phase, but it's a misuse of internal API)
we would possibly need to be able to attach message to 'any' node.
Other thing to notice - current messaging interface in thin-pool
target requires to suspend thin volume origin first and then send
a create message, but this could not have any 'nice' solution on lvm2
side and IMHO we should introduce something like 'create_after_resume'
message.
Patch also changes the moment, where lvm2 transaction id is increased.
Now it happens only after successful finish of kernel transaction id
change. This change was needed to handle properly activation of pool,
which is in the middle of unfinished transaction, and also this corrects
usage of thin-pool by external apps like Docker.
---
WHATS_NEW | 1 +
lib/activate/activate.c | 12 ++++-----
lib/activate/dev_manager.c | 56 ++++++++++++++++++++++++++++++++++++--------
lib/metadata/lv_manip.c | 32 +++++++++++--------------
lib/metadata/thin_manip.c | 44 ++++++++++++++++++----------------
lib/thin/thin.c | 9 ++++---
libdm/libdm-deptree.c | 10 ++++++-
7 files changed, 102 insertions(+), 62 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 22fc42a..382f290 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.124 -
=================================
+ Move sending thin pool messages from resume to suspend phase.
Report warning when pool is overprovisioned and not auto resized.
Recognize free-form date/time values for lv_time field in selection criteria.
Fix regression in select to match string fields if using synonyms (2.02.123).
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
index 5905b7b..2e58c3b 100644
--- a/lib/activate/activate.c
+++ b/lib/activate/activate.c
@@ -1813,7 +1813,9 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
goto_out;
/* Ignore origin_only unless LV is origin in both old and new metadata */
- if (!lv_is_thin_volume(ondisk_lv) && !(lv_is_origin(ondisk_lv) && lv_is_origin(incore_lv)))
+ /* or LV is thin or thin pool volume */
+ if (!lv_is_thin_volume(ondisk_lv) && !lv_is_thin_pool(ondisk_lv) &&
+ !(lv_is_origin(ondisk_lv) && lv_is_origin(incore_lv)))
laopts->origin_only = 0;
if (test_mode()) {
@@ -1987,7 +1989,6 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
const struct logical_volume *lv_to_free = NULL;
struct lvinfo info;
int r = 0;
- int messages_only = 0;
if (!activation())
return 1;
@@ -1995,10 +1996,7 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
goto_out;
- if (lv_is_thin_pool(lv) && laopts->origin_only)
- messages_only = 1;
-
- if (!lv_is_origin(lv) && !lv_is_thin_volume(lv))
+ if (!lv_is_origin(lv) && !lv_is_thin_volume(lv) && !lv_is_thin_pool(lv))
laopts->origin_only = 0;
if (test_mode()) {
@@ -2018,7 +2016,7 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
if (!lv_info(cmd, lv, laopts->origin_only, &info, 0, 0))
goto_out;
- if (!info.exists || !(info.suspended || messages_only)) {
+ if (!info.exists || !info.suspended) {
if (error_if_not_active)
goto_out;
r = 1;
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index f4b2a4a..e13b8e5 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -60,6 +60,7 @@ struct dev_manager {
uint32_t pvmove_mirror_count;
int flush_required;
int activation; /* building activation tree */
+ int suspend; /* building suspend tree */
int skip_external_lv;
struct dm_list pending_delete; /* str_list of dlid(s) with pending delete */
unsigned track_pending_delete;
@@ -2066,6 +2067,11 @@ static int _pool_register_callback(struct dev_manager *dm,
return 1;
}
+/* Declaration to resolve suspend tree and message passing for thin-pool */
+static int _add_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree_node *dnode,
+ struct lv_segment *seg,
+ struct lv_activate_opts *laopts);
/*
* Add LV and any known dependencies
*/
@@ -2134,15 +2140,43 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
*/
if (!_add_dev_to_dtree(dm, dtree, lv, lv_layer(lv)))
return_0;
+
+ /*
+ * TODO: change API and move this code
+ * Could be easier to handle this in _add_dev_to_dtree()
+ * and base this according to info.exists ?
+ */
if (!dm->activation) {
- /* Setup callback for non-activation partial tree */
- /* Activation gets own callback when needed */
- /* TODO: extend _cached_dm_info() to return dnode */
if (!(uuid = build_dm_uuid(dm->mem, lv, lv_layer(lv))))
return_0;
- if ((node = dm_tree_find_node_by_uuid(dtree, uuid)) &&
- !_pool_register_callback(dm, node, lv))
- return_0;
+ if ((node = dm_tree_find_node_by_uuid(dtree, uuid))) {
+ if (origin_only) {
+ struct lv_activate_opts laopts = {
+ .origin_only = 1,
+ .send_messages = 1 /* Node with messages */
+ };
+ /*
+ * Add some messsages if right node exist in the table only
+ * when building SUSPEND tree for origin-only thin-pool.
+ *
+ * TODO: Fix call of '_add_target_to_dtree()' to add message
+ * to thin-pool node as we already know the pool node exists
+ * in the table. Any better/cleaner API way ?
+ *
+ * Probably some 'new' target method to add messages for any node?
+ */
+ if (dm->suspend &&
+ !dm_list_empty(&(first_seg(lv)->thin_messages)) &&
+ !_add_target_to_dtree(dm, node, first_seg(lv), &laopts))
+ return_0;
+ } else {
+ /* Setup callback for non-activation partial tree */
+ /* Activation gets own callback when needed */
+ /* TODO: extend _cached_dm_info() to return dnode */
+ if (!_pool_register_callback(dm, node, lv))
+ return_0;
+ }
+ }
}
}
@@ -2240,7 +2274,7 @@ static struct dm_tree *_create_partial_dtree(struct dev_manager *dm, const struc
dm_tree_set_optional_uuid_suffixes(dtree, &uuid_suffix_list[0]);
- if (!_add_lv_to_dtree(dm, dtree, lv, (lv_is_origin(lv) || lv_is_thin_volume(lv)) ? origin_only : 0))
+ if (!_add_lv_to_dtree(dm, dtree, lv, (lv_is_origin(lv) || lv_is_thin_volume(lv) || lv_is_thin_pool(lv)) ? origin_only : 0))
goto_bad;
return dtree;
@@ -2695,7 +2729,7 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
return_0;
/* Add pool layer */
- if (seg->pool_lv &&
+ if (seg->pool_lv && !laopts->origin_only &&
!_add_new_lv_to_dtree(dm, dtree, seg->pool_lv, laopts,
lv_layer(seg->pool_lv)))
return_0;
@@ -3137,6 +3171,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
}
/* Some targets may build bigger tree for activation */
dm->activation = ((action == PRELOAD) || (action == ACTIVATE));
+ dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND);
if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only)))
return_0;
@@ -3181,7 +3216,9 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
case PRELOAD:
case ACTIVATE:
/* Add all required new devices to tree */
- if (!_add_new_lv_to_dtree(dm, dtree, lv, laopts, (lv_is_origin(lv) && laopts->origin_only) ? "real" : NULL))
+ if (!_add_new_lv_to_dtree(dm, dtree, lv, laopts,
+ (lv_is_origin(lv) && laopts->origin_only) ? "real" :
+ (lv_is_thin_pool(lv) && laopts->origin_only) ? "tpool" : NULL))
goto_out;
/* Preload any devices required before any suspensions */
@@ -3219,7 +3256,6 @@ out_no_root:
int dev_manager_activate(struct dev_manager *dm, const struct logical_volume *lv,
struct lv_activate_opts *laopts)
{
- laopts->send_messages = 1;
if (!_tree_action(dm, lv, laopts, ACTIVATE))
return_0;
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index d23dd62..1891491 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -7372,11 +7372,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
stack;
goto revert_new_lv;
}
- /* When change is activating, don't duplicate backup call */
- if (!is_change_activating(lp->activate))
- backup(vg);
}
- if (is_change_activating(lp->activate)) {
+ if (!dm_list_empty(&first_seg(pool_lv)->thin_messages)) {
/* Send message so that table preload knows new thin */
if (!lv_is_active(pool_lv)) {
/* Avoid multiple thin-pool activations in this case */
@@ -7394,25 +7391,24 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
}
}
/* Keep thin pool active until thin volume is activated */
- if (!update_pool_lv(pool_lv, (thin_pool_was_active < 0) ? 1 : 0)) {
+ if (!update_pool_lv(pool_lv, 1)) {
stack;
goto revert_new_lv;
}
+ }
+ backup(vg);
- backup(vg);
-
- if (!lv_active_change(cmd, lv, lp->activate, 0)) {
- log_error("Failed to activate thin %s.", lv->name);
- goto deactivate_and_revert_new_lv;
- }
+ if (!lv_active_change(cmd, lv, lp->activate, 0)) {
+ log_error("Failed to activate thin %s.", lv->name);
+ goto deactivate_and_revert_new_lv;
+ }
- /* Restore inactive state if needed */
- if (!thin_pool_was_active &&
- !deactivate_lv(cmd, pool_lv)) {
- log_error("Failed to deactivate thin pool %s.",
- display_lvname(pool_lv));
- return NULL;
- }
+ /* Restore inactive state if needed */
+ if (!thin_pool_was_active &&
+ !deactivate_lv(cmd, pool_lv)) {
+ log_error("Failed to deactivate thin pool %s.",
+ display_lvname(pool_lv));
+ return NULL;
}
} else if (lp->snapshot) {
lv->status |= LV_TEMPORARY;
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
index 1453f88..6ae0795 100644
--- a/lib/metadata/thin_manip.c
+++ b/lib/metadata/thin_manip.c
@@ -21,6 +21,7 @@
#include "defaults.h"
#include "display.h"
+/* TODO: drop unused no_update */
int attach_pool_message(struct lv_segment *pool_seg, dm_thin_message_t type,
struct logical_volume *lv, uint32_t delete_id,
int no_update)
@@ -62,10 +63,6 @@ int attach_pool_message(struct lv_segment *pool_seg, dm_thin_message_t type,
tmsg->type = type;
- /* If the 1st message is add in non-read-only mode, modify transaction_id */
- if (!no_update && dm_list_empty(&pool_seg->thin_messages))
- pool_seg->transaction_id++;
-
dm_list_add(&pool_seg->thin_messages, &tmsg->list);
log_debug_metadata("Added %s message.",
@@ -423,7 +420,7 @@ static int _check_pool_create(const struct logical_volume *lv)
int update_pool_lv(struct logical_volume *lv, int activate)
{
- int monitored;
+ int monitored = DMEVENTD_MONITOR_IGNORE;
int ret = 1;
if (!lv_is_thin_pool(lv)) {
@@ -449,32 +446,37 @@ int update_pool_lv(struct logical_volume *lv, int activate)
display_lvname(lv));
return 0;
}
-
- if (!(ret = _check_pool_create(lv)))
- stack;
-
+ } else
+ activate = 0; /* Was already active */
+
+ if (!(ret = _check_pool_create(lv)))
+ stack; /* Safety guard, needs local presence of thin-pool target */
+ else if (!(ret = suspend_lv_origin(lv->vg->cmd, lv)))
+ /* Send messages */
+ log_error("Failed to suspend and send message %s.", display_lvname(lv));
+ else if (!(ret = resume_lv_origin(lv->vg->cmd, lv)))
+ log_error("Failed to resume %s.", display_lvname(lv));
+
+ if (activate) {
if (!deactivate_lv(lv->vg->cmd, lv)) {
init_dmeventd_monitor(monitored);
return_0;
}
init_dmeventd_monitor(monitored);
-
- /* Unlock memory if possible */
- memlock_unlock(lv->vg->cmd);
}
- /*
- * Resume active pool to send thin messages.
- * origin_only is used to skip check for resumed state
- */
- else if (!resume_lv_origin(lv->vg->cmd, lv)) {
- log_error("Failed to resume %s.", lv->name);
- return 0;
- } else if (!(ret = _check_pool_create(lv)))
- stack;
+
+ /* Unlock memory if possible */
+ memlock_unlock(lv->vg->cmd);
+
+ if (!ret)
+ return_0;
}
dm_list_init(&(first_seg(lv)->thin_messages));
+ /* thin-pool target transaction is finished, increase lvm2 TID */
+ first_seg(lv)->transaction_id++;
+
if (!vg_write(lv->vg) || !vg_commit(lv->vg))
return_0;
diff --git a/lib/thin/thin.c b/lib/thin/thin.c
index e098744..6993249 100644
--- a/lib/thin/thin.c
+++ b/lib/thin/thin.c
@@ -294,7 +294,8 @@ static int _thin_pool_add_target_line(struct dev_manager *dm,
return 0;
}
- if (!dm_tree_node_add_thin_pool_target(node, len, seg->transaction_id,
+ if (!dm_tree_node_add_thin_pool_target(node, len,
+ seg->transaction_id + (laopts->send_messages ? 1 : 0),
metadata_dlid, pool_dlid,
seg->chunk_size, seg->low_water_mark,
seg->zero_new_blocks ? 0 : 1))
@@ -372,11 +373,11 @@ static int _thin_pool_add_target_line(struct dev_manager *dm,
if (!dm_list_empty(&seg->thin_messages)) {
/* Messages were passed, modify transaction_id as the last one */
- log_debug_activation("Thin pool set transaction id %" PRIu64 ".", seg->transaction_id);
+ log_debug_activation("Thin pool set transaction id %" PRIu64 ".", seg->transaction_id + 1);
if (!dm_tree_node_add_thin_pool_message(node,
DM_THIN_MESSAGE_SET_TRANSACTION_ID,
- seg->transaction_id - 1,
- seg->transaction_id))
+ seg->transaction_id,
+ seg->transaction_id + 1))
return_0;
}
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index b9a8582..5baac6d 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -241,7 +241,12 @@ struct load_properties {
*/
unsigned delay_resume_if_new;
- /* Send messages for this node in preload */
+ /*
+ * Call node_send_messages(), set to 2 if there are messages
+ * When != 0, it validates matching transaction id, thus thin-pools
+ * where transation_id is passed as 0 are never validated, this
+ * allows external managment of thin-pool TID.
+ */
unsigned send_messages;
/* Skip suspending node's children, used when sending messages to thin-pool */
int skip_suspend;
@@ -3871,7 +3876,8 @@ int dm_tree_node_add_thin_pool_message(struct dm_tree_node *node,
tm->message.type = type;
dm_list_add(&seg->thin_messages, &tm->list);
- node->props.send_messages = 1;
+ /* Higher value >1 identifies there are really some messages */
+ node->props.send_messages = 2;
return 1;
}
8 years, 9 months
master - libdm: support for posting messages in suspend
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=5bef18f2eb1071...
Commit: 5bef18f2eb1071a29b682f6f456a13a73493868e
Parent: 622064f00fe41a02ab857ce02920a9e8818e610b
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Wed Jul 1 13:30:14 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jul 3 16:13:14 2015 +0200
libdm: support for posting messages in suspend
Add support for sending message in suspend tree for thin-pools.
When this operation is requested whole subtree suspend is then skipped.
This is experimantal support for new lvm2 code for sending message
in suspend phase where 'thin-pool origin-only suspend' will send
messages instead of really suspending thin-pool tree.
When suspening thin volume origin-only - only thin volume is suspended,
then messages are posted and thin-pool suspend is skipped.
---
WHATS_NEW_DM | 1 +
libdm/libdm-deptree.c | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 5e1f25f..f4537d9 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,6 @@
Version 1.02.101 -
=================================
+ Add experimental support to passing messages in suspend tree.
Add dm_report_value_cache_{set,get} to support caching during report/select.
Add dm_report_reserved_handler to handle report reserved value actions.
Support dynamic value in select: DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE.
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index e344d1a..b9a8582 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -243,6 +243,8 @@ struct load_properties {
/* Send messages for this node in preload */
unsigned send_messages;
+ /* Skip suspending node's children, used when sending messages to thin-pool */
+ int skip_suspend;
};
/* Two of these used to join two nodes with uses and used_by. */
@@ -1768,6 +1770,19 @@ int dm_tree_suspend_children(struct dm_tree_node *dnode,
!info.exists || info.suspended)
continue;
+ /* If child has some real messages send them */
+ if ((child->props.send_messages > 1) && r) {
+ if (!(r = _node_send_messages(child, uuid_prefix, uuid_prefix_len, 1)))
+ stack;
+ else {
+ log_debug_activation("Sent messages to thin-pool %s."
+ "skipping suspend of its children.",
+ _node_name(child));
+ child->props.skip_suspend++;
+ }
+ continue;
+ }
+
if (!_suspend_node(name, info.major, info.minor,
child->dtree->skip_lockfs,
child->dtree->no_flush, &newinfo)) {
@@ -1786,6 +1801,9 @@ int dm_tree_suspend_children(struct dm_tree_node *dnode,
handle = NULL;
while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (child->props.skip_suspend)
+ continue;
+
if (!(uuid = dm_tree_node_get_uuid(child))) {
stack;
continue;
8 years, 9 months
master - thin: check for overprovisioning
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=622064f00fe41a...
Commit: 622064f00fe41a02ab857ce02920a9e8818e610b
Parent: 9cee94372a7e45b6a40478ec17de1c142ed268d4
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Fri Jul 3 15:31:31 2015 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Fri Jul 3 16:13:14 2015 +0200
thin: check for overprovisioning
---
WHATS_NEW | 1 +
lib/metadata/lv_manip.c | 5 ++
lib/metadata/metadata.h | 1 +
lib/metadata/thin_manip.c | 88 +++++++++++++++++++++++++++++++++++
test/shell/thin-overprovisioning.sh | 71 ++++++++++++++++++++++++++++
5 files changed, 166 insertions(+), 0 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 6a33e6d..22fc42a 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.124 -
=================================
+ Report warning when pool is overprovisioned and not auto resized.
Recognize free-form date/time values for lv_time field in selection criteria.
Fix regression in select to match string fields if using synonyms (2.02.123).
Fix regression when printing more lv names via display_lvname (2.02.122).
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 87888a7..d23dd62 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -5167,6 +5167,8 @@ static struct logical_volume *_lvresize_volume(struct cmd_context *cmd,
lp->extents - lv->le_count,
pvh, alloc, lp->approx_alloc))
return_NULL;
+ else if (!pool_check_overprovisioning(lv))
+ return_NULL;
if (old_extents == lv->le_count)
log_print_unless_silent("Size of logical volume %s unchanged from %s (%" PRIu32 " extents).",
@@ -7293,6 +7295,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
return_NULL;
}
+ if (!pool_check_overprovisioning(lv))
+ return_NULL;
+
/* FIXME Log allocation and attachment should have happened inside lv_extend. */
if (lp->log_count &&
!seg_is_raid(first_seg(lv)) && seg_is_mirrored(first_seg(lv))) {
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index abf6072..1a5306b 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -485,6 +485,7 @@ int lv_is_merging_thin_snapshot(const struct logical_volume *lv);
int pool_has_message(const struct lv_segment *seg,
const struct logical_volume *lv, uint32_t device_id);
int pool_below_threshold(const struct lv_segment *pool_seg);
+int pool_check_overprovisioning(const struct logical_volume *lv);
int create_pool(struct logical_volume *lv, const struct segment_type *segtype,
struct alloc_handle *ah, uint32_t stripes, uint32_t stripe_size);
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
index 1620d62..1453f88 100644
--- a/lib/metadata/thin_manip.c
+++ b/lib/metadata/thin_manip.c
@@ -237,6 +237,94 @@ int pool_below_threshold(const struct lv_segment *pool_seg)
}
/*
+ * Detect overprovisioning and check lvm2 is configured for auto resize.
+ *
+ * If passed LV is thin volume/pool, check first only this one for overprovisiong.
+ * Lots of test combined together.
+ * Test is not detecting status of dmeventd, too complex for now...
+ */
+int pool_check_overprovisioning(const struct logical_volume *lv)
+{
+ const struct lv_list *lvl;
+ const struct seg_list *sl;
+ const struct logical_volume *pool_lv = NULL;
+ struct cmd_context *cmd = lv->vg->cmd;
+ const char *txt = "";
+ uint64_t thinsum = 0, poolsum = 0, sz = ~0;
+ int threshold, max_threshold = 0;
+ int percent, min_percent = 100;
+ int more_pools = 0;
+
+ /* When passed thin volume, check related pool first */
+ if (lv_is_thin_volume(lv))
+ pool_lv = first_seg(lv)->pool_lv;
+ else if (lv_is_thin_pool(lv))
+ pool_lv = lv;
+
+ if (pool_lv) {
+ poolsum += pool_lv->size;
+ dm_list_iterate_items(sl, &pool_lv->segs_using_this_lv)
+ thinsum += sl->seg->lv->size;
+
+ if (thinsum <= poolsum)
+ return 1; /* All thins fit into this thin pool */
+ }
+
+ /* Sum all thins and all thin pools in VG */
+ dm_list_iterate_items(lvl, &lv->vg->lvs) {
+ if (!lv_is_thin_pool(lvl->lv))
+ continue;
+
+ threshold = find_config_tree_int(cmd, activation_thin_pool_autoextend_threshold_CFG,
+ lv_config_profile(lvl->lv));
+ percent = find_config_tree_int(cmd, activation_thin_pool_autoextend_percent_CFG,
+ lv_config_profile(lvl->lv));
+ if (threshold > max_threshold)
+ max_threshold = threshold;
+ if (percent < min_percent)
+ min_percent = percent;
+
+ if (lvl->lv == pool_lv)
+ continue; /* Skip iteration for already checked thin pool */
+
+ more_pools++;
+ poolsum += lvl->lv->size;
+ dm_list_iterate_items(sl, &lvl->lv->segs_using_this_lv)
+ thinsum += sl->seg->lv->size;
+ }
+
+ if (thinsum <= poolsum)
+ return 1; /* All fits for all pools */
+
+ if ((sz = vg_size(lv->vg)) < thinsum)
+ /* Thin sum size is above VG size */
+ txt = " and the size of whole volume group";
+ else if ((sz = vg_free(lv->vg)) < thinsum)
+ /* Thin sum size is more then free space in a VG */
+ txt = !sz ? "" : " and the amount of free space in volume group";
+ else if ((max_threshold > 99) || !min_percent)
+ /* There is some free space in VG, but it is not configured
+ * for growing - threshold is 100% or percent is 0% */
+ sz = poolsum;
+
+ if (sz != ~0) {
+ log_warn("WARNING: Sum of all thin volume sizes (%s) exceeds the "
+ "size of thin pool%s%s%s (%s)!",
+ display_size(cmd, thinsum),
+ more_pools ? "" : " ",
+ more_pools ? "s" : display_lvname(pool_lv),
+ txt,
+ (sz > 0) ? display_size(cmd, sz) : "no free space in volume group");
+ if (max_threshold > 99)
+ log_print_unless_silent("For thin pool auto extension activation/thin_pool_autoextend_threshold should be bellow 100.");
+ if (!min_percent)
+ log_print_unless_silent("For thin pool auto extension activation/thin_pool_autoextend_percent should be above 0.");
+ }
+
+ return 1;
+}
+
+/*
* Validate given external origin could be used with thin pool
*/
int pool_supports_external_origin(const struct lv_segment *pool_seg, const struct logical_volume *external_lv)
diff --git a/test/shell/thin-overprovisioning.sh b/test/shell/thin-overprovisioning.sh
new file mode 100644
index 0000000..ac0b96c
--- /dev/null
+++ b/test/shell/thin-overprovisioning.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# Copyright (C) 2015 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Test warns when thin pool is overprovisiong
+
+export LVM_TEST_THIN_REPAIR_CMD=${LVM_TEST_THIN_REPAIR_CMD-/bin/false}
+
+. lib/inittest
+
+aux have_thin 1 3 0 || skip
+
+# 2PVs by 32M
+aux prepare_vg 2 33
+
+lvcreate -L32 -T $vg/pool
+
+# leave 12M free space
+lvcreate -an -n $lv1 -L16 $vg 2>&1 | tee out
+vgs $vg
+
+lvcreate -n thin1 -V30 $vg/pool 2>&1 | tee out
+not grep "WARNING: Sum" out
+
+# Pool gets overprovisioned
+lvcreate -an -n thin2 -V4 $vg/pool 2>&1 | tee out
+grep "WARNING: Sum" out
+grep "amount of free space in volume group (12.00 MiB)" out
+
+# Eat all space in VG
+lvcreate -an -n $lv2 -L12 $vg 2>&1 | tee out
+grep "WARNING: Sum" out
+grep "no free space in volume group" out
+
+lvcreate -an -n thin3 -V1G $vg/pool 2>&1 | tee out
+grep "WARNING: Sum" out
+grep "the size of whole volume group" out
+
+lvremove -ff $vg/thin2 $vg/thin3 $vg/$lv2
+
+# Create 2nd thin pool in a VG
+
+lvcreate -L4 -T $vg/pool2
+lvcreate -V4 -n thin2 $vg/pool2 2>&1 | tee out
+not grep "WARNING: Sum" out
+
+lvcreate -an -V4 -n thin3 $vg/pool2 2>&1 | tee out
+grep "WARNING: Sum of all thin volume sizes (38.00 MiB)" out
+grep "free space in volume group (6.00 MiB)" out
+
+lvcreate -an -L6 -n $lv3 $vg 2>&1 | tee out
+grep "no free space in volume group" out
+
+lvremove -ff $vg/thin2 $vg/thin3
+
+lvcreate -an -V4 -n thin2 $vg/pool2 2>&1 | tee out
+not grep "WARNING: Sum" out
+
+# Check if resize notices problem
+lvextend -L+8 $vg/thin2
+
+vgs $vg
+
+vgremove -ff $vg
8 years, 9 months
master - report: select: add handler to recognize fuzzy time specification
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9cee94372a7e45...
Commit: 9cee94372a7e45b6a40478ec17de1c142ed268d4
Parent: 3b1422c45c6c782f5134e74439ccdd6766085d4c
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Fri Jul 3 10:43:07 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Fri Jul 3 10:51:31 2015 +0200
report: select: add handler to recognize fuzzy time specification
Recognize date and time specification within selection criteria
that is formulated in a more free-form way besides to the original
basic YYYY-MM-DD HH:MM format that libdevmapper supports.
Currently, this free-form format is recognized for lv_time field.
Users are able to use expressions from this set:
- weekday names ("Sunday" - "Saturday" or abbreviated as "Sun" - "Sat")
- labels for points in time ("noon", "midnight")
- labels for a day relative to current day ("today", "yesterday")
- points back in time with relative offset from today (N is a number)
( "N" "seconds"/"minutes"/"hours"/"days"/"weeks"/"years" "ago")
( "N" "secs"/"mins"/"hrs" ... "ago")
( "N" "s"/"m"/"h" ... "ago")
- time specification either in hh:mm:ss format or with AM/PM suffixes
- month names ("January" - "December" or abbreviated as "Jan" - "Dec")
For example:
$ date
Fri Jul 3 10:11:13 CEST 2015
$ lvmconfig --type full report/time_format
time_format="%a %Y-%m-%d %T %z %Z [%s]"
$ lvs
LV VG Time
lvol0 vg Fri 2014-08-22 21:25:41 +0200 CEST [1408735541]
lvol2 vg Sun 2015-04-26 14:52:20 +0200 CEST [1430052740]
root fedora Wed 2015-05-27 08:09:21 +0200 CEST [1432706961]
swap fedora Wed 2015-05-27 08:09:21 +0200 CEST [1432706961]
lvol1 vg Tue 2015-06-30 03:25:43 +0200 CEST [1435627543]
lvol3 vg Tue 2015-06-30 14:52:23 +0200 CEST [1435668743]
lvol6 vg Wed 2015-07-01 13:35:56 +0200 CEST [1435750556]
lvol4 vg Thu 2015-07-02 12:12:02 +0200 CEST [1435831922]
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
$ lvs -S 'time=yesterday'
LV VG Time
lvol4 vg Thu 2015-07-02 12:12:02 +0200 CEST [1435831922]
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
$ lvs -S 'time since "June 30"'
LV VG Time
lvol1 vg Tue 2015-06-30 03:25:43 +0200 CEST [1435627543]
lvol3 vg Tue 2015-06-30 14:52:23 +0200 CEST [1435668743]
lvol6 vg Wed 2015-07-01 13:35:56 +0200 CEST [1435750556]
lvol4 vg Thu 2015-07-02 12:12:02 +0200 CEST [1435831922]
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
$ lvs -S 'time since "noon June 30"'
LV VG Time
lvol3 vg Tue 2015-06-30 14:52:23 +0200 CEST [1435668743]
lvol6 vg Wed 2015-07-01 13:35:56 +0200 CEST [1435750556]
lvol4 vg Thu 2015-07-02 12:12:02 +0200 CEST [1435831922]
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
$ lvs -S 'time since "2 July 9AM"'
LV VG Time
lvol4 vg Thu 2015-07-02 12:12:02 +0200 CEST [1435831922]
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
$ lvs -S 'time since "2 July 1PM"'
LV VG Time
lvol5 vg Thu 2015-07-02 14:30:32 +0200 CEST [1435840232]
...and so on.
---
WHATS_NEW | 1 +
lib/report/report.c | 1034 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/report/values.h | 1 +
libdm/libdm-report.c | 6 +-
4 files changed, 1040 insertions(+), 2 deletions(-)
diff --git a/WHATS_NEW b/WHATS_NEW
index 6c822c4..6a33e6d 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.124 -
=================================
+ Recognize free-form date/time values for lv_time field in selection criteria.
Fix regression in select to match string fields if using synonyms (2.02.123).
Fix regression when printing more lv names via display_lvname (2.02.122).
Add missing error logging to unlock_vg and sync_local_dev_names callers.
diff --git a/lib/report/report.c b/lib/report/report.c
index 4d1b8eb..80d189a 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -27,6 +27,7 @@
#include <stddef.h> /* offsetof() */
#include <float.h> /* DBL_MAX */
+#include <time.h>
struct lvm_report_object {
struct volume_group *vg;
@@ -64,6 +65,1039 @@ static const double _siz_max = DBL_MAX;
*/
static const int32_t _reserved_num_undef_32 = INT32_C(-1);
+typedef enum {
+ /* top-level identification */
+ TIME_NULL,
+ TIME_NUM,
+ TIME_STR,
+
+ /* direct numeric value */
+ TIME_NUM__START,
+ TIME_NUM_MULTIPLIER,
+ TIME_NUM_MULTIPLIER_NEGATIVE,
+ TIME_NUM_DAY,
+ TIME_NUM_YEAR,
+ TIME_NUM__END,
+
+ /* direct string value */
+ TIME_STR_TIMEZONE,
+
+ /* time frame strings */
+ TIME_FRAME__START,
+ TIME_FRAME_AGO,
+ TIME_FRAME__END,
+
+ /* labels for dates */
+ TIME_LABEL_DATE__START,
+
+ TIME_LABEL_DATE_TODAY,
+ TIME_LABEL_DATE_YESTERDAY,
+
+ /* weekday name strings */
+ TIME_WEEKDAY__START,
+ TIME_WEEKDAY_SUNDAY,
+ TIME_WEEKDAY_MONDAY,
+ TIME_WEEKDAY_TUESDAY,
+ TIME_WEEKDAY_WEDNESDAY,
+ TIME_WEEKDAY_THURSDAY,
+ TIME_WEEKDAY_FRIDAY,
+ TIME_WEEKDAY_SATURDAY,
+ TIME_WEEKDAY__END,
+
+ TIME_LABEL_DATE__END,
+
+ /* labels for times */
+ TIME_LABEL_TIME__START,
+ TIME_LABEL_TIME_NOON,
+ TIME_LABEL_TIME_MIDNIGHT,
+ TIME_LABEL_TIME__END,
+
+ /* time unit strings */
+ TIME_UNIT__START,
+ TIME_UNIT_SECOND,
+ TIME_UNIT_SECOND_REL,
+ TIME_UNIT_MINUTE,
+ TIME_UNIT_MINUTE_REL,
+ TIME_UNIT_HOUR,
+ TIME_UNIT_HOUR_REL,
+ TIME_UNIT_AM,
+ TIME_UNIT_PM,
+ TIME_UNIT_DAY,
+ TIME_UNIT_WEEK,
+ TIME_UNIT_MONTH,
+ TIME_UNIT_YEAR,
+ TIME_UNIT_TZ_MINUTE,
+ TIME_UNIT_TZ_HOUR,
+ TIME_UNIT__END,
+
+ /* month name strings */
+ TIME_MONTH__START,
+ TIME_MONTH_JANUARY,
+ TIME_MONTH_FEBRUARY,
+ TIME_MONTH_MARCH,
+ TIME_MONTH_APRIL,
+ TIME_MONTH_MAY,
+ TIME_MONTH_JUNE,
+ TIME_MONTH_JULY,
+ TIME_MONTH_AUGUST,
+ TIME_MONTH_SEPTEMBER,
+ TIME_MONTH_OCTOBER,
+ TIME_MONTH_NOVEMBER,
+ TIME_MONTH_DECEMBER,
+ TIME_MONTH__END,
+} time_id_t;
+
+#define TIME_PROP_DATE 0x00000001 /* date-related */
+#define TIME_PROP_TIME 0x00000002 /* time-related */
+#define TIME_PROP_ABS 0x00000004 /* absolute value */
+#define TIME_PROP_REL 0x00000008 /* relative value */
+
+struct time_prop {
+ time_id_t id;
+ uint32_t prop_flags;
+ time_id_t granularity;
+};
+
+#define ADD_TIME_PROP(id, flags, granularity) [id] = {id, flags, granularity},
+
+static const struct time_prop _time_props[] = {
+ ADD_TIME_PROP(TIME_NULL, 0, TIME_NULL)
+ ADD_TIME_PROP(TIME_NUM, 0, TIME_NULL)
+ ADD_TIME_PROP(TIME_STR, 0, TIME_NULL)
+
+ ADD_TIME_PROP(TIME_NUM_MULTIPLIER, 0, TIME_NULL)
+ ADD_TIME_PROP(TIME_NUM_MULTIPLIER_NEGATIVE, 0, TIME_NULL)
+ ADD_TIME_PROP(TIME_NUM_DAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_NUM_YEAR, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_YEAR)
+
+ ADD_TIME_PROP(TIME_STR_TIMEZONE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
+
+ ADD_TIME_PROP(TIME_FRAME_AGO, TIME_PROP_DATE | TIME_PROP_TIME | TIME_PROP_REL, TIME_NULL)
+
+ ADD_TIME_PROP(TIME_LABEL_DATE_TODAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_LABEL_DATE_YESTERDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_SUNDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_MONDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_TUESDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_WEDNESDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_THURSDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_FRIDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_WEEKDAY_SATURDAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_DAY)
+
+ ADD_TIME_PROP(TIME_LABEL_TIME_NOON, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
+ ADD_TIME_PROP(TIME_LABEL_TIME_MIDNIGHT, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
+
+ ADD_TIME_PROP(TIME_UNIT_SECOND, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_SECOND)
+ ADD_TIME_PROP(TIME_UNIT_SECOND_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_SECOND)
+ ADD_TIME_PROP(TIME_UNIT_MINUTE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_MINUTE)
+ ADD_TIME_PROP(TIME_UNIT_MINUTE_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_MINUTE)
+ ADD_TIME_PROP(TIME_UNIT_HOUR, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
+ ADD_TIME_PROP(TIME_UNIT_HOUR_REL, TIME_PROP_TIME | TIME_PROP_REL, TIME_UNIT_HOUR)
+ ADD_TIME_PROP(TIME_UNIT_AM, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
+ ADD_TIME_PROP(TIME_UNIT_PM, TIME_PROP_TIME | TIME_PROP_ABS, TIME_UNIT_HOUR)
+ ADD_TIME_PROP(TIME_UNIT_DAY, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_DAY)
+ ADD_TIME_PROP(TIME_UNIT_WEEK, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_WEEK)
+ ADD_TIME_PROP(TIME_UNIT_MONTH, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_UNIT_YEAR, TIME_PROP_DATE | TIME_PROP_REL, TIME_UNIT_YEAR)
+ ADD_TIME_PROP(TIME_UNIT_TZ_MINUTE, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
+ ADD_TIME_PROP(TIME_UNIT_TZ_HOUR, TIME_PROP_TIME | TIME_PROP_ABS, TIME_NULL)
+
+ ADD_TIME_PROP(TIME_MONTH_JANUARY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_FEBRUARY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_MARCH, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_APRIL, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_MAY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_JUNE, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_JULY, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_AUGUST, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_SEPTEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_OCTOBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_NOVEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+ ADD_TIME_PROP(TIME_MONTH_DECEMBER, TIME_PROP_DATE | TIME_PROP_ABS, TIME_UNIT_MONTH)
+};
+
+#define TIME_REG_PLURAL_S 0x00000001 /* also recognize plural form with "s" suffix */
+
+struct time_reg {
+ const char *name;
+ const struct time_prop *prop;
+ uint32_t reg_flags;
+};
+
+#define TIME_PROP(id) (_time_props + id)
+
+static const struct time_reg _time_reg[] = {
+ /*
+ * Group of tokens representing time frame and used
+ * with relative date/time to specify different flavours
+ * of relativity.
+ */
+ {"ago", TIME_PROP(TIME_FRAME_AGO), 0},
+
+ /*
+ * Group of tokens labeling some date and used
+ * instead of direct absolute specification.
+ */
+ {"today", TIME_PROP(TIME_LABEL_DATE_TODAY), 0}, /* 0:00 - 23:59:59 for current date */
+ {"yesterday", TIME_PROP(TIME_LABEL_DATE_YESTERDAY), 0}, /* 0:00 - 23:59:59 for current date minus 1 day*/
+
+ /*
+ * Group of tokens labeling some date - weekday
+ * names used to build up date.
+ */
+ {"Sunday", TIME_PROP(TIME_WEEKDAY_SUNDAY), TIME_REG_PLURAL_S},
+ {"Sun", TIME_PROP(TIME_WEEKDAY_SUNDAY), 0},
+ {"Monday", TIME_PROP(TIME_WEEKDAY_MONDAY), TIME_REG_PLURAL_S},
+ {"Mon", TIME_PROP(TIME_WEEKDAY_MONDAY), 0},
+ {"Tuesday", TIME_PROP(TIME_WEEKDAY_TUESDAY), TIME_REG_PLURAL_S},
+ {"Tue", TIME_PROP(TIME_WEEKDAY_TUESDAY), 0},
+ {"Wednesday", TIME_PROP(TIME_WEEKDAY_WEDNESDAY), TIME_REG_PLURAL_S},
+ {"Wed", TIME_PROP(TIME_WEEKDAY_WEDNESDAY), 0},
+ {"Thursday", TIME_PROP(TIME_WEEKDAY_THURSDAY), TIME_REG_PLURAL_S},
+ {"Thu", TIME_PROP(TIME_WEEKDAY_THURSDAY), 0},
+ {"Friday", TIME_PROP(TIME_WEEKDAY_FRIDAY), TIME_REG_PLURAL_S},
+ {"Fri", TIME_PROP(TIME_WEEKDAY_FRIDAY), 0},
+ {"Saturday", TIME_PROP(TIME_WEEKDAY_SATURDAY), TIME_REG_PLURAL_S},
+ {"Sat", TIME_PROP(TIME_WEEKDAY_SATURDAY), 0},
+
+ /*
+ * Group of tokens labeling some time and used
+ * instead of direct absolute specification.
+ */
+ {"noon", TIME_PROP(TIME_LABEL_TIME_NOON), TIME_REG_PLURAL_S}, /* 12:00:00 */
+ {"midnight", TIME_PROP(TIME_LABEL_TIME_MIDNIGHT), TIME_REG_PLURAL_S}, /* 00:00:00 */
+
+ /*
+ * Group of tokens used to build up time. Most of these
+ * are used either as relative or absolute time units.
+ * The absolute ones are always used with TIME_FRAME_*
+ * token, otherwise the unit is relative.
+ */
+ {"second", TIME_PROP(TIME_UNIT_SECOND), TIME_REG_PLURAL_S},
+ {"sec", TIME_PROP(TIME_UNIT_SECOND), TIME_REG_PLURAL_S},
+ {"s", TIME_PROP(TIME_UNIT_SECOND), 0},
+ {"minute", TIME_PROP(TIME_UNIT_MINUTE), TIME_REG_PLURAL_S},
+ {"min", TIME_PROP(TIME_UNIT_MINUTE), TIME_REG_PLURAL_S},
+ {"m", TIME_PROP(TIME_UNIT_MINUTE), 0},
+ {"hour", TIME_PROP(TIME_UNIT_HOUR), TIME_REG_PLURAL_S},
+ {"hr", TIME_PROP(TIME_UNIT_HOUR), TIME_REG_PLURAL_S},
+ {"h", TIME_PROP(TIME_UNIT_HOUR), 0},
+ {"AM", TIME_PROP(TIME_UNIT_AM), 0},
+ {"PM", TIME_PROP(TIME_UNIT_PM), 0},
+
+ /*
+ * Group of tokens used to build up date.
+ * These are all relative ones.
+ */
+ {"day", TIME_PROP(TIME_UNIT_DAY), TIME_REG_PLURAL_S},
+ {"week", TIME_PROP(TIME_UNIT_WEEK), TIME_REG_PLURAL_S},
+ {"month", TIME_PROP(TIME_UNIT_MONTH), TIME_REG_PLURAL_S},
+ {"year", TIME_PROP(TIME_UNIT_YEAR), TIME_REG_PLURAL_S},
+ {"yr", TIME_PROP(TIME_UNIT_YEAR), TIME_REG_PLURAL_S},
+
+ /*
+ * Group of tokes used to build up date.
+ * These are all absolute.
+ */
+ {"January", TIME_PROP(TIME_MONTH_JANUARY), 0},
+ {"Jan", TIME_PROP(TIME_MONTH_JANUARY), 0},
+ {"February", TIME_PROP(TIME_MONTH_FEBRUARY), 0},
+ {"Feb", TIME_PROP(TIME_MONTH_FEBRUARY), 0},
+ {"March", TIME_PROP(TIME_MONTH_MARCH), 0},
+ {"Mar", TIME_PROP(TIME_MONTH_MARCH), 0},
+ {"April", TIME_PROP(TIME_MONTH_APRIL), 0},
+ {"Apr", TIME_PROP(TIME_MONTH_APRIL), 0},
+ {"May", TIME_PROP(TIME_MONTH_MAY), 0},
+ {"June", TIME_PROP(TIME_MONTH_JUNE), 0},
+ {"Jun", TIME_PROP(TIME_MONTH_JUNE), 0},
+ {"July", TIME_PROP(TIME_MONTH_JULY), 0},
+ {"Jul", TIME_PROP(TIME_MONTH_JULY), 0},
+ {"August", TIME_PROP(TIME_MONTH_AUGUST), 0},
+ {"Aug", TIME_PROP(TIME_MONTH_AUGUST), 0},
+ {"September", TIME_PROP(TIME_MONTH_SEPTEMBER), 0},
+ {"Sep", TIME_PROP(TIME_MONTH_SEPTEMBER), 0},
+ {"October", TIME_PROP(TIME_MONTH_OCTOBER), 0},
+ {"Oct", TIME_PROP(TIME_MONTH_OCTOBER), 0},
+ {"November", TIME_PROP(TIME_MONTH_NOVEMBER), 0},
+ {"Nov", TIME_PROP(TIME_MONTH_NOVEMBER), 0},
+ {"December", TIME_PROP(TIME_MONTH_DECEMBER), 0},
+ {"Dec", TIME_PROP(TIME_MONTH_DECEMBER), 0},
+ {NULL, TIME_PROP(TIME_NULL), 0},
+};
+
+struct time_item {
+ struct dm_list list;
+ const struct time_prop *prop;
+ const char *s;
+ size_t len;
+};
+
+struct time_info {
+ struct dm_pool *mem;
+ struct dm_list *ti_list;
+ time_t *now;
+ time_id_t min_abs_date_granularity;
+ time_id_t max_abs_date_granularity;
+ time_id_t min_abs_time_granularity;
+ time_id_t min_rel_time_granularity;
+};
+
+static int _is_time_num(time_id_t id)
+{
+ return ((id > TIME_NUM__START) && (id < TIME_NUM__END));
+};
+
+/*
+static int _is_time_frame(time_id_t id)
+{
+ return ((id > TIME_FRAME__START) && (id < TIME_FRAME__END));
+};
+*/
+
+static int _is_time_label_date(time_id_t id)
+{
+ return ((id > TIME_LABEL_DATE__START) && (id < TIME_LABEL_DATE__END));
+};
+
+static int _is_time_label_time(time_id_t id)
+{
+ return ((id > TIME_LABEL_TIME__START) && (id < TIME_LABEL_TIME__END));
+};
+
+static int _is_time_unit(time_id_t id)
+{
+ return ((id > TIME_UNIT__START) && (id < TIME_UNIT__END));
+};
+
+static int _is_time_weekday(time_id_t id)
+{
+ return ((id > TIME_WEEKDAY__START) && (id < TIME_WEEKDAY__END));
+};
+
+static int _is_time_month(time_id_t id)
+{
+ return ((id > TIME_MONTH__START) && (id < TIME_MONTH__END));
+};
+
+static const char *_skip_space(const char *s)
+{
+ while (*s && isspace(*s))
+ s++;
+ return s;
+}
+
+/* Move till delim or space */
+static const char *_move_till_item_end(const char *s)
+{
+ char c = *s;
+ int is_num = isdigit(c);
+
+ /*
+ * Allow numbers to be attached to next token, for example
+ * it's correct to write "12 hours" as well as "12hours".
+ */
+ while (c && !isspace(c) && (is_num ? (is_num = isdigit(c)) : 1))
+ c = *++s;
+
+ return s;
+}
+
+static struct time_item *_alloc_time_item(struct dm_pool *mem, time_id_t id,
+ const char *s, size_t len)
+{
+ struct time_item *ti;
+
+ if (!(ti = dm_pool_zalloc(mem, sizeof(struct time_item)))) {
+ log_error("alloc_time_item: dm_pool_zalloc failed");
+ return NULL;
+ }
+
+ ti->prop = &_time_props[id];
+ ti->s = s;
+ ti->len = len;
+
+ return ti;
+}
+
+static int _add_time_part_to_list(struct dm_pool *mem, struct dm_list *list,
+ time_id_t id, int minus, const char *s, size_t len)
+{
+ struct time_item *ti1, *ti2;
+
+ if (!(ti1 = _alloc_time_item(mem, minus ? TIME_NUM_MULTIPLIER_NEGATIVE
+ : TIME_NUM_MULTIPLIER, s, len)) ||
+ !(ti2 = _alloc_time_item(mem, id, s + len, 0)))
+ return 0;
+ dm_list_add(list, &ti1->list);
+ dm_list_add(list, &ti2->list);
+
+ return 1;
+}
+
+static int _get_time(struct dm_pool *mem, const char **str,
+ struct dm_list *list, int tz)
+{
+ const char *end, *s = *str;
+ int r = 0;
+
+ /* hour */
+ end = _move_till_item_end(s);
+ if (!_add_time_part_to_list(mem, list, tz ? TIME_UNIT_TZ_HOUR : TIME_UNIT_HOUR,
+ tz == -1, s, end - s))
+ goto out;
+
+ /* minute */
+ if (*end != ':')
+ /* minute required */
+ goto out;
+ s = end + 1;
+ end = _move_till_item_end(s);
+ if (!_add_time_part_to_list(mem, list, tz ? TIME_UNIT_TZ_MINUTE : TIME_UNIT_MINUTE,
+ tz == -1, s, end - s))
+ goto out;
+
+ /* second */
+ if (*end != ':') {
+ /* second not required */
+ s = end + 1;
+ r = 1;
+ goto out;
+ } else if (tz)
+ /* timezone does not have seconds */
+ goto out;
+
+ s = end + 1;
+ end = _move_till_item_end(s);
+ if (!_add_time_part_to_list(mem, list, TIME_UNIT_SECOND, 0, s, end - s))
+ goto out;
+
+ s = end + 1;
+ r = 1;
+out:
+ *str = s;
+ return r;
+}
+
+static int _preparse_fuzzy_time(const char *s, struct time_info *info)
+{
+ struct dm_list *list;
+ struct time_item *ti;
+ const char *end;
+ int fuzzy = 0;
+ time_id_t id;
+ size_t len;
+ int r = 0;
+ char c;
+
+ if (!(list = dm_pool_alloc(info->mem, sizeof(struct dm_list)))) {
+ log_error("_preparse_fuzzy_time: dm_pool_alloc failed");
+ goto out;
+ }
+ dm_list_init(list);
+ s = _skip_space(s);
+
+ while ((c = *s)) {
+ /*
+ * If the string consists of -:+, digits or spaces,
+ * it's not worth looking for fuzzy names here -
+ * it's standard YYYY-MM-DD HH:MM:SS +-HH:MM format
+ * and that is parseable by libdm directly.
+ */
+ if (!(isdigit(c) || (c == '-') || (c == ':') || (c == '+')))
+ fuzzy = 1;
+
+ end = _move_till_item_end(s);
+
+ if (isalpha(c))
+ id = TIME_STR;
+ else if (isdigit(c)) {
+ if (*end == ':') {
+ /* we have time */
+ if (!_get_time(info->mem, &s, list, 0))
+ goto out;
+ continue;
+ }
+ /* we have some other number */
+ id = TIME_NUM;
+ } else if ((c == '-') || (c == '+')) {
+ s++;
+ /* we have timezone */
+ if (!_get_time(info->mem, &s, list, (c == '-') ? -1 : 1))
+ goto out;
+ continue;
+ } else
+ goto out;
+
+ len = end - s;
+ if (!(ti = _alloc_time_item(info->mem, id, s, len)))
+ goto out;
+ dm_list_add(list, &ti->list);
+ s += len;
+ s = _skip_space(s);
+ }
+
+ info->ti_list = list;
+ r = 1;
+out:
+ if (!(r && fuzzy)) {
+ dm_pool_free(info->mem, list);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _match_time_str(struct dm_list *ti_list, struct time_item *ti)
+{
+ struct time_item *ti_context_p = (struct time_item *) dm_list_prev(ti_list, &ti->list);
+ size_t reg_len;
+ int i;
+
+ ti->prop = TIME_PROP(TIME_NULL);
+
+ for (i = 0; _time_reg[i].name; i++) {
+ reg_len = strlen(_time_reg[i].name);
+ if ((ti->len != reg_len) &&
+ !((_time_reg[i].reg_flags & TIME_REG_PLURAL_S) &&
+ (ti->len == reg_len+1) && (ti->s[reg_len] == 's')))
+ continue;
+
+ if (!strncasecmp(ti->s, _time_reg[i].name, reg_len)) {
+ ti->prop = _time_reg[i].prop;
+ if ((ti->prop->id > TIME_UNIT__START) && (ti->prop->id < TIME_UNIT__END) &&
+ ti_context_p && (ti_context_p->prop->id == TIME_NUM))
+ ti_context_p->prop = TIME_PROP(TIME_NUM_MULTIPLIER);
+ break;
+ }
+ }
+
+ return ti->prop->id;
+}
+
+static int _match_time_num(struct dm_list *ti_list, struct time_item *ti)
+{
+ struct time_item *ti_context_p = (struct time_item *) dm_list_prev(ti_list, &ti->list);
+ struct time_item *ti_context_n = (struct time_item *) dm_list_next(ti_list, &ti->list);
+ struct time_item *ti_context_nn = ti_context_n ? (struct time_item *) dm_list_next(ti_list, &ti_context_n->list) : NULL;
+
+ if (ti_context_n &&
+ (ti_context_n->prop->id > TIME_MONTH__START) &&
+ (ti_context_n->prop->id < TIME_MONTH__END)) {
+ if (ti_context_nn && ti_context_nn->prop->id == TIME_NUM) {
+ if (ti->len < ti_context_nn->len) {
+ /* 24 Feb 2015 */
+ ti->prop = TIME_PROP(TIME_NUM_DAY);
+ ti_context_nn->prop = TIME_PROP(TIME_NUM_YEAR);
+ } else {
+ /* 2015 Feb 24 */
+ ti->prop = TIME_PROP(TIME_NUM_YEAR);
+ ti_context_nn->prop = TIME_PROP(TIME_NUM_DAY);
+ }
+ } else {
+ if (ti->len <= 2)
+ /* 24 Feb */
+ ti->prop = TIME_PROP(TIME_NUM_DAY);
+ else
+ /* 2015 Feb */
+ ti->prop = TIME_PROP(TIME_NUM_YEAR);
+ }
+ } else if (ti_context_p &&
+ (ti_context_p->prop->id > TIME_MONTH__START) &&
+ (ti_context_p->prop->id < TIME_MONTH__END)) {
+ if (ti->len <= 2)
+ /* Feb 24 */
+ ti->prop = TIME_PROP(TIME_NUM_DAY);
+ else
+ /* Feb 2015 */
+ ti->prop = TIME_PROP(TIME_NUM_YEAR);
+ } else
+ ti->prop = TIME_PROP(TIME_NUM_YEAR);
+
+ return ti->prop->id;
+}
+
+static void _detect_time_granularity(struct time_info *info, struct time_item *ti)
+{
+ time_id_t gran = ti->prop->granularity;
+ int is_date, is_abs, is_rel;
+
+ if (gran == TIME_NULL)
+ return;
+
+ is_date = ti->prop->prop_flags & TIME_PROP_DATE;
+ is_abs = ti->prop->prop_flags & TIME_PROP_ABS;
+ is_rel = ti->prop->prop_flags & TIME_PROP_REL;
+
+ if (is_date && is_abs) {
+ if (gran > info->max_abs_date_granularity)
+ info->max_abs_date_granularity = gran;
+ if (gran < info->min_abs_date_granularity)
+ info->min_abs_date_granularity = gran;
+ } else {
+ if (is_abs && (gran < info->min_abs_time_granularity))
+ info->min_abs_time_granularity = gran;
+ else if (is_rel && (gran < info->min_rel_time_granularity))
+ info->min_rel_time_granularity = gran;
+ }
+}
+
+static void _change_to_relative(struct time_info *info, struct time_item *ti)
+{
+ struct time_item *ti2;
+
+ ti2 = ti;
+ while ((ti2 = (struct time_item *) dm_list_prev(info->ti_list, &ti2->list))) {
+ if (ti2->prop->id == TIME_FRAME_AGO)
+ break;
+
+ switch (ti2->prop->id) {
+ case TIME_UNIT_SECOND:
+ ti2->prop = TIME_PROP(TIME_UNIT_SECOND_REL);
+ break;
+ case TIME_UNIT_MINUTE:
+ ti2->prop = TIME_PROP(TIME_UNIT_MINUTE_REL);
+ break;
+ case TIME_UNIT_HOUR:
+ ti2->prop = TIME_PROP(TIME_UNIT_HOUR_REL);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int _recognize_time_items(struct time_info *info)
+{
+ struct time_item *ti;
+
+ /*
+ * At first, try to recognize strings.
+ * Also, if there are any items which may be absolute or
+ * relative and we have "TIME_FRAME_AGO", change them to relative.
+ */
+ dm_list_iterate_items(ti, info->ti_list) {
+ if ((ti->prop->id == TIME_STR) && !_match_time_str(info->ti_list, ti)) {
+ log_error("Unrecognized string in date/time "
+ "specification at \"%s\".", ti->s);
+ return 0;
+ }
+ if (ti->prop->id == TIME_FRAME_AGO)
+ _change_to_relative(info, ti);
+ }
+
+ /*
+ * Now, recognize any numbers and be sensitive to the context
+ * given by strings we recognized before. Also, detect time
+ * granularity used (both for absolute and/or relative parts).
+ */
+ dm_list_iterate_items(ti, info->ti_list) {
+ if ((ti->prop->id == TIME_NUM) && !_match_time_num(info->ti_list, ti)) {
+ log_error("Unrecognized number in date/time "
+ "specification at \"%s\".", ti->s);
+ return 0;
+ }
+ _detect_time_granularity(info, ti);
+ }
+
+ return 1;
+}
+
+static int _check_time_items(struct time_info *info)
+{
+ struct time_item *ti;
+ uint32_t flags;
+ int rel;
+ int date_is_relative = -1, time_is_relative = -1;
+ int label_time = 0, label_date = 0;
+
+ dm_list_iterate_items(ti, info->ti_list) {
+ flags = ti->prop->prop_flags;
+ rel = flags & TIME_PROP_REL;
+
+ if (flags & TIME_PROP_DATE) {
+ if (date_is_relative < 0)
+ date_is_relative = rel;
+ else if ((date_is_relative ^ rel) &&
+ (info->max_abs_date_granularity >= info->min_rel_time_granularity)) {
+ log_error("Mixed absolute and relative date "
+ "specification found at \"%s\".", ti->s);
+ return 0;
+ }
+
+ /* Date label can be used only once and not mixed with other date spec. */
+ if (label_date) {
+ log_error("Ambiguous date specification found at \"%s\".", ti->s);
+ return 0;
+ } else if (_is_time_label_date(ti->prop->id))
+ label_date = 1;
+ }
+
+ else if (flags & TIME_PROP_TIME) {
+ if (time_is_relative < 0)
+ time_is_relative = rel;
+ else if ((time_is_relative ^ rel)) {
+ log_error("Mixed absolute and relative time "
+ "specification found at \"%s\".", ti->s);
+ return 0;
+ }
+
+ /* Time label can be used only once and not mixed with other time spec. */
+ if (label_time) {
+ log_error("Ambiguous time specification found at \"%s\".", ti->s);
+ return 0;
+ } else if (_is_time_label_time(ti->prop->id))
+ label_time = 1;
+ }
+ }
+
+ return 1;
+}
+
+#define CACHE_ID_TIME_NOW "time_now"
+
+static time_t *_get_now(struct dm_report *rh, struct dm_pool *mem)
+{
+ const void *cached_obj;
+ time_t *now;
+
+ if (!(cached_obj = dm_report_value_cache_get(rh, CACHE_ID_TIME_NOW))) {
+ if (!(now = dm_pool_zalloc(mem, sizeof(time_t)))) {
+ log_error("_get_now: dm_pool_zalloc failed");
+ return NULL;
+ }
+ time(now);
+ if (!dm_report_value_cache_set(rh, CACHE_ID_TIME_NOW, now)) {
+ log_error("_get_now: failed to cache current time");
+ return NULL;
+ }
+ } else
+ now = (time_t *) cached_obj;
+
+ return now;
+}
+
+static void _adjust_time_for_granularity(struct time_info *info, struct tm *tm, time_t *t)
+{
+ switch (info->min_abs_date_granularity) {
+ case TIME_UNIT_YEAR:
+ tm->tm_mon = 0;
+ /* fall through */
+ case TIME_UNIT_MONTH:
+ tm->tm_mday = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (info->min_abs_time_granularity) {
+ case TIME_UNIT_HOUR:
+ tm->tm_min = 0;
+ /* fall through */
+ case TIME_UNIT_MINUTE:
+ tm->tm_sec = 0;
+ break;
+ case TIME_UNIT__END:
+ if (info->min_rel_time_granularity == TIME_UNIT__END)
+ tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
+ break;
+ default:
+ break;
+ }
+
+ if ((info->min_abs_time_granularity == TIME_UNIT__END) &&
+ (info->min_rel_time_granularity >= TIME_UNIT_DAY) &&
+ (info->min_rel_time_granularity <= TIME_UNIT_YEAR))
+ tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
+}
+
+#define SECS_PER_MINUTE 60
+#define SECS_PER_HOUR 3600
+#define SECS_PER_DAY 86400
+
+static int _days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static int _is_leap_year(long year)
+{
+ return (((year % 4==0) && (year % 100 != 0)) || (year % 400 == 0));
+}
+
+static int _get_days_in_month(long month, long year)
+{
+ return (month == 2 && _is_leap_year(year)) ? _days_in_month[month-1] + 1
+ : _days_in_month[month-1];
+}
+
+static void _get_resulting_time_span(struct time_info *info,
+ struct tm *tm, time_t t,
+ time_t *t_result1, time_t *t_result2)
+{
+ time_t t1 = mktime(tm) - t;
+ time_t t2 = t1;
+ struct tm tmp;
+
+ if (info->min_abs_time_granularity != TIME_UNIT__END) {
+ if (info->min_abs_time_granularity == TIME_UNIT_MINUTE)
+ t2 += (SECS_PER_MINUTE - 1);
+ else if (info->min_abs_time_granularity == TIME_UNIT_HOUR)
+ t2 += (SECS_PER_HOUR - 1);
+ } else if (info->min_rel_time_granularity != TIME_UNIT__END) {
+ if (info->min_rel_time_granularity == TIME_UNIT_MINUTE)
+ t1 -= (SECS_PER_MINUTE + 1);
+ else if (info->min_rel_time_granularity == TIME_UNIT_HOUR)
+ t1 -= (SECS_PER_HOUR + 1);
+ else if ((info->min_rel_time_granularity >= TIME_UNIT_DAY) &&
+ (info->min_rel_time_granularity <= TIME_UNIT_YEAR))
+ t2 += (SECS_PER_DAY - 1);
+ } else {
+ if (info->min_abs_date_granularity == TIME_UNIT_MONTH)
+ t2 += (SECS_PER_DAY * _get_days_in_month(tm->tm_mon + 1, tm->tm_year) - 1);
+ else if (info->min_abs_date_granularity != TIME_UNIT__END)
+ t2 += (SECS_PER_DAY - 1);
+ }
+
+ /* Adjust for DST if needed. */
+ localtime_r(&t1, &tmp);
+ if (tmp.tm_isdst)
+ t1 -= SECS_PER_HOUR;
+ localtime_r(&t2, &tmp);
+ if (tmp.tm_isdst)
+ t2 -= SECS_PER_HOUR;
+
+ *t_result1 = t1;
+ *t_result2 = t2;
+}
+
+static int _translate_time_items(struct dm_report *rh, struct time_info *info,
+ const char **data_out)
+{
+ struct time_item *ti, *ti_p = NULL;
+ long multiplier = 1;
+ struct tm tm_now;
+ time_id_t id;
+ char *end;
+ long num;
+ struct tm tm; /* absolute time */
+ time_t t = 0; /* offset into past before absolute time */
+ time_t t1, t2;
+ char buf[32];
+
+ localtime_r(info->now, &tm_now);
+ tm = tm_now;
+ tm.tm_isdst = 0; /* we'll adjust for dst later */
+ tm.tm_wday = tm.tm_yday = -1;
+
+ dm_list_iterate_items(ti, info->ti_list) {
+ id = ti->prop->id;
+
+ if (_is_time_num(id)) {
+ num = strtol(ti->s, &end, 10);
+ switch (id) {
+ case TIME_NUM_MULTIPLIER_NEGATIVE:
+ multiplier = -num;
+ break;
+ case TIME_NUM_MULTIPLIER:
+ multiplier = num;
+ break;
+ case TIME_NUM_DAY:
+ tm.tm_mday = num;
+ break;
+ case TIME_NUM_YEAR:
+ tm.tm_year = num - 1900;
+ break;
+ default:
+ break;
+ }
+ } else if (_is_time_month(id)) {
+ tm.tm_mon = id - TIME_MONTH__START - 1;
+ } else if (_is_time_label_date(id)) {
+ if (_is_time_weekday(id)) {
+ num = id - TIME_WEEKDAY__START - 1;
+ if (tm_now.tm_wday < num)
+ num = 7 - num + tm_now.tm_wday;
+ else
+ num = tm_now.tm_wday - num;
+ t += num * SECS_PER_DAY;
+ } else switch (id) {
+ case TIME_LABEL_DATE_YESTERDAY:
+ t += SECS_PER_DAY;
+ break;
+ case TIME_LABEL_DATE_TODAY:
+ /* Nothing to do here - we started with today. */
+ break;
+ default:
+ break;
+ }
+ } else if (_is_time_label_time(id)) {
+ switch (id) {
+ case TIME_LABEL_TIME_NOON:
+ tm.tm_hour = 12;
+ tm.tm_min = tm.tm_sec = 0;
+ break;
+ case TIME_LABEL_TIME_MIDNIGHT:
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ break;
+ default:
+ break;
+ }
+ } else if (_is_time_unit(id)) {
+ switch (id) {
+ case TIME_UNIT_SECOND:
+ tm.tm_sec = multiplier;
+ break;
+ case TIME_UNIT_SECOND_REL:
+ t += multiplier;
+ break;
+ case TIME_UNIT_MINUTE:
+ tm.tm_min = multiplier;
+ break;
+ case TIME_UNIT_MINUTE_REL:
+ t += (multiplier * SECS_PER_MINUTE);
+ break;
+ case TIME_UNIT_HOUR:
+ tm.tm_hour = multiplier;
+ break;
+ case TIME_UNIT_HOUR_REL:
+ t += (multiplier * SECS_PER_HOUR);
+ break;
+ case TIME_UNIT_AM:
+ if (ti_p->prop->id == TIME_NUM_MULTIPLIER)
+ tm.tm_hour = multiplier;
+ break;
+ case TIME_UNIT_PM:
+ if (_is_time_unit(ti_p->prop->id))
+ t -= 12 * SECS_PER_HOUR;
+ else if (ti_p->prop->id == TIME_NUM_MULTIPLIER)
+ tm.tm_hour = multiplier + 12;
+ break;
+ case TIME_UNIT_DAY:
+ t += multiplier * SECS_PER_DAY;
+ break;
+ case TIME_UNIT_WEEK:
+ t += multiplier * 7 * SECS_PER_DAY;
+ break;
+ case TIME_UNIT_MONTH:
+ /* if months > 12, convert to years first */
+ num = multiplier / 12;
+ tm.tm_year -= num;
+
+ num = multiplier % 12;
+ if (num > (tm.tm_mon + 1)) {
+ tm.tm_year--;
+ tm.tm_mon = 12 - num + tm.tm_mon;
+ } else
+ tm.tm_mon -= num;
+ break;
+ case TIME_UNIT_YEAR:
+ tm.tm_year -= multiplier;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ti_p = ti;
+ }
+
+ _adjust_time_for_granularity(info, &tm, &t);
+ _get_resulting_time_span(info, &tm, t, &t1, &t2);
+
+ dm_pool_free(info->mem, info->ti_list);
+ info->ti_list = NULL;
+
+ dm_snprintf(buf, sizeof(buf), "@%ld:@%ld", t1, t2);
+ if (!(*data_out = dm_pool_strdup(info->mem, buf))) {
+ log_error("_translate_time_items: dm_pool_strdup failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static const char *_lv_time_handler_parse_fuzzy_name(struct dm_report *rh,
+ struct dm_pool *mem,
+ const char *data_in)
+{
+ const char *s = data_in;
+ const char *data_out = NULL;
+ struct time_info info = {.mem = mem,
+ .ti_list = NULL,
+ .now = _get_now(rh, mem),
+ .min_abs_date_granularity = TIME_UNIT__END,
+ .max_abs_date_granularity = TIME_UNIT__START,
+ .min_abs_time_granularity = TIME_UNIT__END,
+ .min_rel_time_granularity = TIME_UNIT__END};
+
+ if (!info.now)
+ goto_out;
+
+ /* recognize top-level parts - string/number/time/timezone? */
+ if (!_preparse_fuzzy_time(s, &info))
+ goto out;
+
+ /* recognize each part in more detail, also look at the context around if needed */
+ if (!_recognize_time_items(&info))
+ goto out;
+
+ /* check if the combination of items is allowed or whether it makes sense at all */
+ if (!_check_time_items(&info))
+ goto out;
+
+ /* translate items into final time range */
+ if (!_translate_time_items(rh, &info, &data_out))
+ goto out;
+out:
+ if (info.ti_list)
+ dm_pool_free(info.mem, info.ti_list);
+ return data_out;
+}
+
+static void *_lv_time_handler_get_dynamic_value(struct dm_report *rh,
+ struct dm_pool *mem,
+ const char *data_in)
+{
+ time_t t1, t2;
+ time_t *result;
+
+ if (sscanf(data_in, "@%ld:@%ld", &t1, &t2) != 2) {
+ log_error("Failed to get value for parsed time specification.");
+ return NULL;
+ }
+
+ if (!(result = dm_pool_alloc(mem, 2 * sizeof(time_t)))) {
+ log_error("Failed to allocate space to store time range.");
+ return NULL;
+ }
+
+ result[0] = t1;
+ result[1] = t2;
+
+ return result;
+}
+
+static int lv_time_handler(struct dm_report *rh, struct dm_pool *mem,
+ uint32_t field_num,
+ dm_report_reserved_action_t action,
+ const void *data_in, const void **data_out)
+{
+ *data_out = NULL;
+ if (!data_in)
+ return 1;
+
+ switch (action) {
+ case DM_REPORT_RESERVED_PARSE_FUZZY_NAME:
+ *data_out = _lv_time_handler_parse_fuzzy_name(rh, mem, data_in);
+ break;
+ case DM_REPORT_RESERVED_GET_DYNAMIC_VALUE:
+ if (!(*data_out = _lv_time_handler_get_dynamic_value(rh, mem, data_in)))
+ return 0;
+ break;
+ default:
+ return -1;
+ }
+
+ return 1;
+}
+
/*
* Get type reserved value - the value returned is the direct value of that type.
*/
diff --git a/lib/report/values.h b/lib/report/values.h
index 742cb50..728760c 100644
--- a/lib/report/values.h
+++ b/lib/report/values.h
@@ -87,6 +87,7 @@ FIELD_RESERVED_VALUE(NOFLAG, lv_read_ahead, lv_read_ahead_auto, "", &_siz_max, "
FIELD_RESERVED_VALUE(NAMED, lv_when_full, lv_when_full_error, "", "error", "error", "error when full", "error if no space")
FIELD_RESERVED_VALUE(NAMED, lv_when_full, lv_when_full_queue, "", "queue", "queue", "queue when full", "queue if no space")
FIELD_RESERVED_VALUE(NOFLAG, lv_when_full, lv_when_full_undef, "", "", "", "undefined")
+FIELD_RESERVED_VALUE(NAMED | RANGE | FUZZY | DYNAMIC, lv_time, lv_time_fuzzy, "", lv_time_handler, NULL)
/* Reserved values for SEG fields */
FIELD_RESERVED_VALUE(NOFLAG, cache_policy, cache_policy_undef, "", "", "", "undefined")
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 2ec98d6..7afbb8d 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -2273,11 +2273,13 @@ static int _check_reserved_values_supported(const struct dm_report_field_type fi
static uint32_t supported_reserved_types = DM_REPORT_FIELD_TYPE_NUMBER |
DM_REPORT_FIELD_TYPE_SIZE |
DM_REPORT_FIELD_TYPE_PERCENT |
- DM_REPORT_FIELD_TYPE_STRING;
+ DM_REPORT_FIELD_TYPE_STRING |
+ DM_REPORT_FIELD_TYPE_TIME;
static uint32_t supported_reserved_types_with_range = DM_REPORT_FIELD_RESERVED_VALUE_RANGE |
DM_REPORT_FIELD_TYPE_NUMBER |
DM_REPORT_FIELD_TYPE_SIZE |
- DM_REPORT_FIELD_TYPE_PERCENT;
+ DM_REPORT_FIELD_TYPE_PERCENT |
+ DM_REPORT_FIELD_TYPE_TIME;
if (!reserved_values)
8 years, 9 months
master - report: call appropriate handler to evaluate fuzzy reserved names and dynamic reserved values
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=3b1422c45c6c78...
Commit: 3b1422c45c6c782f5134e74439ccdd6766085d4c
Parent: 335707b0e2e657a40ae87fb47dbf744cb53d74fb
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Wed May 20 18:47:54 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Fri Jul 3 10:47:38 2015 +0200
report: call appropriate handler to evaluate fuzzy reserved names and dynamic reserved values
Wire the dm_report_reserved_handler instance call in reporting/selection
infrastructure to handle reserved value actions (currently only
DM_REPORT_RESERVED_PARSE_FUZZY_NAME and DM_REPORT_RESERVED_GET_DYNAMIC_VALUE
actions).
---
lib/report/report.c | 8 +++++
libdm/libdm-report.c | 71 +++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/lib/report/report.c b/lib/report/report.c
index 2e9a5d3..4d1b8eb 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -96,6 +96,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
* - 'reserved_value_id_n' (for 0)
*/
#define NUM uint64_t
+#define NUM_HND dm_report_reserved_handler
+#define HND (dm_report_reserved_handler)
#define NOFLAG 0
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
@@ -117,6 +119,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
#include "values.h"
#undef NUM
+#undef NUM_HND
+#undef HND
#undef NOFLAG
#undef NAMED
#undef RANGE
@@ -134,6 +138,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
*/
#define NUM DM_REPORT_FIELD_TYPE_NUMBER
+#define NUM_HND DM_REPORT_FIELD_TYPE_NUMBER
+#define HND 0
#define NOFLAG 0
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
@@ -154,6 +160,8 @@ static const struct dm_report_reserved_value _report_reserved_values[] = {
};
#undef NUM
+#undef NUM_HND
+#undef HND
#undef NOFLAG
#undef NAMED
#undef RANGE
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 559d463..2ec98d6 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -2123,14 +2123,48 @@ static const char *_tok_value_string(const char *s,
return s;
}
-static const char *_reserved_name(const char **names, const char *s, size_t len)
+static const char *_reserved_name(struct dm_report *rh,
+ const struct dm_report_reserved_value *reserved,
+ const struct dm_report_field_reserved_value *frv,
+ uint32_t field_num, const char *s, size_t len)
{
- const char **name = names;
+ dm_report_reserved_handler handler;
+ const char *canonical_name;
+ const char **name;
+ char *tmp_s;
+ char c;
+ int r;
+
+ name = reserved->names;
while (*name) {
if ((strlen(*name) == len) && !strncmp(*name, s, len))
return *name;
name++;
}
+
+ if (reserved->type & DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES) {
+ handler = (dm_report_reserved_handler) frv ? frv->value : reserved->value;
+ c = s[len];
+ tmp_s = (char *) s;
+ tmp_s[len] = '\0';
+ if ((r = handler(rh, rh->selection->mem, field_num,
+ DM_REPORT_RESERVED_PARSE_FUZZY_NAME,
+ tmp_s, (const void **) &canonical_name)) <= 0) {
+ if (r == -1)
+ log_error(INTERNAL_ERROR "%s reserved value handler for field %s has missing "
+ "implementation of DM_REPORT_RESERVED_PARSE_FUZZY_NAME action",
+ (reserved->type & DM_REPORT_FIELD_TYPE_MASK) ? "type-specific" : "field-specific",
+ rh->fields[field_num].id);
+ else
+ log_error("Error occured while processing %s reserved value handler for field %s",
+ (reserved->type & DM_REPORT_FIELD_TYPE_MASK) ? "type-specific" : "field-specific",
+ rh->fields[field_num].id);
+ }
+ tmp_s[len] = c;
+ if (r && canonical_name)
+ return canonical_name;
+ }
+
return NULL;
}
@@ -2144,6 +2178,7 @@ static const char *_get_reserved(struct dm_report *rh, unsigned type,
struct reserved_value_wrapper *rvw)
{
const struct dm_report_reserved_value *iter = implicit ? NULL : rh->reserved_values;
+ const struct dm_report_field_reserved_value *frv;
const char *tmp_begin, *tmp_end, *tmp_s = s;
const char *name = NULL;
char c;
@@ -2160,12 +2195,14 @@ static const char *_get_reserved(struct dm_report *rh, unsigned type,
while (iter->value) {
if (!(iter->type & DM_REPORT_FIELD_TYPE_MASK)) {
/* DM_REPORT_FIELD_TYPE_NONE - per-field reserved value */
- if (((((const struct dm_report_field_reserved_value *) iter->value)->field_num) == field_num) &&
- (name = _reserved_name(iter->names, tmp_begin, tmp_end - tmp_begin)))
+ frv = (const struct dm_report_field_reserved_value *) iter->value;
+ if ((frv->field_num == field_num) && (name = _reserved_name(rh, iter, frv, field_num,
+ tmp_begin, tmp_end - tmp_begin)))
break;
} else if (iter->type & type) {
/* DM_REPORT_FIELD_TYPE_* - per-type reserved value */
- if ((name = _reserved_name(iter->names, tmp_begin, tmp_end - tmp_begin)))
+ if ((name = _reserved_name(rh, iter, NULL, field_num,
+ tmp_begin, tmp_end - tmp_begin)))
break;
}
iter++;
@@ -3115,6 +3152,8 @@ static int _get_reserved_value(struct dm_report *rh, uint32_t field_num,
struct reserved_value_wrapper *rvw)
{
const void *tmp_value;
+ dm_report_reserved_handler handler;
+ int r;
if (!rvw->reserved) {
rvw->value = NULL;
@@ -3122,10 +3161,30 @@ static int _get_reserved_value(struct dm_report *rh, uint32_t field_num,
}
if (rvw->reserved->type & DM_REPORT_FIELD_TYPE_MASK)
+ /* type reserved value */
tmp_value = rvw->reserved->value;
else
+ /* per-field reserved value */
tmp_value = ((const struct dm_report_field_reserved_value *) rvw->reserved->value)->value;
+ if (rvw->reserved->type & (DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE | DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES)) {
+ handler = (dm_report_reserved_handler) tmp_value;
+ if ((r = handler(rh, rh->selection->mem, field_num,
+ DM_REPORT_RESERVED_GET_DYNAMIC_VALUE,
+ rvw->matched_name, &tmp_value) <= 0)) {
+ if (r == -1)
+ log_error(INTERNAL_ERROR "%s reserved value handler for field %s has missing"
+ "implementation of DM_REPORT_RESERVED_GET_DYNAMIC_VALUE action",
+ (rvw->reserved->type) & DM_REPORT_FIELD_TYPE_MASK ? "type-specific" : "field-specific",
+ rh->fields[field_num].id);
+ else
+ log_error("Error occured while processing %s reserved value handler for field %s",
+ (rvw->reserved->type) & DM_REPORT_FIELD_TYPE_MASK ? "type-specific" : "field-specific",
+ rh->fields[field_num].id);
+ return 0;
+ }
+ }
+
rvw->value = tmp_value;
return 1;
}
8 years, 9 months
master - report: add infrastructure to recognize fuzzy reserved names and returning dynamic reserved values
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=335707b0e2e657...
Commit: 335707b0e2e657a40ae87fb47dbf744cb53d74fb
Parent: 82ecfa6f0eea0e09dadc9b6e32f6d1359152997f
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Tue May 19 13:01:48 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Fri Jul 3 10:47:09 2015 +0200
report: add infrastructure to recognize fuzzy reserved names and returning dynamic reserved values
With fuzzy names we mean the names for which it's hard or even impossible
to enumerate all possible variations of the name - the name needs to
be evaluated. An example of fuzzy name is a name which has a base
(substring) which matches and it can contain arbitrary variations
around this base. We can cover human language better with fuzzy
names as people may use several different names (or sentences) to
denote the same thing.
With dynamic values we mean the values which are not constants
and they need to be evaluated in runtime. An example of dynamic
value is a value which depends on current system state (e.g. time,
current configuration or any other state which may change and it
needs runtime evaluation).
There's a handler that can be registered with reporting/selection
using dm_report_reserved_handler instance. This is a central point
in which the computation/evaluation happens when processing reserved
values. Currently, there are two actions declared:
DM_REPORT_RESERVED_PARSE_FUZZY_NAME
(translates fuzzy name into canonical name)
DM_REPORT_RESERVED_GET_DYNAMIC_VALUE
(gets value for canonical name)
The handler is then registered as value in struct
dm_report_reserved_value (see explaining comments besided
the struct dm_report_reserved_value in libdevmapper.h).
Also, this patch provides support for simple caching of values
used during report/selection via dm_report_value_cache_{set,get}.
This is supposed to be used mainly in the dm_report_reserved_handler
instances to save values among calls so all the handler calls work
with the same base value used in computation/evaluation and/or
possibly to save resources if the evaluation is more time-consuming.
The cache is attached to the dm_report handle and so the cache is
dropped one dm_report is dropped.
---
WHATS_NEW_DM | 4 ++
lib/report/report.c | 8 +++++
libdm/.exported_symbols.DM_1_02_101 | 2 +
libdm/libdevmapper.h | 54 ++++++++++++++++++++++++++++++++--
libdm/libdm-report.c | 18 +++++++++++
5 files changed, 82 insertions(+), 4 deletions(-)
diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM
index 38045d6..5e1f25f 100644
--- a/WHATS_NEW_DM
+++ b/WHATS_NEW_DM
@@ -1,5 +1,9 @@
Version 1.02.101 -
=================================
+ Add dm_report_value_cache_{set,get} to support caching during report/select.
+ Add dm_report_reserved_handler to handle report reserved value actions.
+ Support dynamic value in select: DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE.
+ Support fuzzy names in select: DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES.
Thin pool trace messages show a device name and major:minor.
Version 1.02.100 - 30th June 2015
diff --git a/lib/report/report.c b/lib/report/report.c
index 40f947f..2e9a5d3 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -99,6 +99,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
#define NOFLAG 0
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
+#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
+#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) \
static const char *_reserved_ ## id ## _names[] = { __VA_ARGS__, NULL}; \
@@ -121,6 +123,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
#undef TYPE_RESERVED_VALUE
#undef FIELD_RESERVED_VALUE
#undef FIELD_RESERVED_BINARY_VALUE
+#undef FUZZY
+#undef DYNAMIC
/*
* Create array of reserved values to be registered with reporting code via
@@ -133,6 +137,8 @@ static const int32_t _reserved_num_undef_32 = INT32_C(-1);
#define NOFLAG 0
#define NAMED DM_REPORT_FIELD_RESERVED_VALUE_NAMED
#define RANGE DM_REPORT_FIELD_RESERVED_VALUE_RANGE
+#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
+#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) {type | flags, &_reserved_ ## id, _reserved_ ## id ## _names, desc},
@@ -151,6 +157,8 @@ static const struct dm_report_reserved_value _report_reserved_values[] = {
#undef NOFLAG
#undef NAMED
#undef RANGE
+#undef FUZZY
+#undef DYNAMIC
#undef TYPE_RESERVED_VALUE
#undef FIELD_RESERVED_VALUE
#undef FIELD_RESERVED_BINARY_VALUE
diff --git a/libdm/.exported_symbols.DM_1_02_101 b/libdm/.exported_symbols.DM_1_02_101
new file mode 100644
index 0000000..75089ba
--- /dev/null
+++ b/libdm/.exported_symbols.DM_1_02_101
@@ -0,0 +1,2 @@
+dm_report_value_cache_set
+dm_report_value_cache_get
diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h
index 7e30d8e..4ce2e6b 100644
--- a/libdm/libdevmapper.h
+++ b/libdm/libdevmapper.h
@@ -1688,6 +1688,8 @@ struct dm_report_field;
#define DM_REPORT_FIELD_RESERVED_VALUE_MASK 0x0000000F
#define DM_REPORT_FIELD_RESERVED_VALUE_NAMED 0x00000001 /* only named value, less strict form of reservation */
#define DM_REPORT_FIELD_RESERVED_VALUE_RANGE 0x00000002 /* value is range - low and high value defined */
+#define DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE 0x00000004 /* value is computed in runtime */
+#define DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES 0x00000008 /* value names are recognized in runtime */
#define DM_REPORT_FIELD_TYPE_ID_LEN 32
#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
@@ -1721,7 +1723,8 @@ struct dm_report_field_reserved_value {
};
/*
- * Reserved value is a 'value' that is used directly if any of the 'names' is hit.
+ * Reserved value is a 'value' that is used directly if any of the 'names' is hit
+ * or in case of fuzzy names, if such fuzzy name matches.
*
* If type is any of DM_REPORT_FIELD_TYPE_*, the reserved value is recognized
* for all fields of that type.
@@ -1736,16 +1739,59 @@ struct dm_report_field_reserved_value {
struct dm_report_reserved_value {
const uint32_t type; /* DM_REPORT_FIELD_RESERVED_VALUE_* and DM_REPORT_FIELD_TYPE_* */
const void *value; /* reserved value:
- struct dm_report_field_reserved_value for DM_REPORT_FIELD_TYPE_NONE
uint64_t for DM_REPORT_FIELD_TYPE_NUMBER
uint64_t for DM_REPORT_FIELD_TYPE_SIZE (number of 512-byte sectors)
uint64_t for DM_REPORT_FIELD_TYPE_PERCENT
- const char * for DM_REPORT_FIELD_TYPE_STRING */
- const char **names; /* null-terminated array of names for this reserved value */
+ const char* for DM_REPORT_FIELD_TYPE_STRING
+ struct dm_report_field_reserved_value for DM_REPORT_FIELD_TYPE_NONE
+ dm_report_reserved_handler* if DM_REPORT_FIELD_RESERVED_VALUE_{DYNAMIC_VALUE,FUZZY_NAMES} is used */
+ const char **names; /* null-terminated array of static names for this reserved value */
const char *description; /* description of the reserved value */
};
/*
+ * Available actions for dm_report_reserved_value_handler.
+ */
+typedef enum {
+ DM_REPORT_RESERVED_PARSE_FUZZY_NAME,
+ DM_REPORT_RESERVED_GET_DYNAMIC_VALUE,
+} dm_report_reserved_action_t;
+
+/*
+ * Generic reserved value handler to process reserved value names and/or values.
+ *
+ * Actions and their input/output:
+ *
+ * DM_REPORT_RESERVED_PARSE_FUZZY_NAME
+ * data_in: const char *fuzzy_name
+ * data_out: const char *canonical_name, NULL if fuzzy_name not recognized
+ *
+ * DM_REPORT_RESERVED_GET_DYNAMIC_VALUE
+ * data_in: const char *canonical_name
+ * data_out: void *value, NULL if canonical_name not recognized
+ *
+ * All actions return:
+ *
+ * -1 if action not implemented
+ * 0 on error
+ * 1 on success
+ */
+typedef int (*dm_report_reserved_handler) (struct dm_report *rh,
+ struct dm_pool *mem,
+ uint32_t field_num,
+ dm_report_reserved_action_t action,
+ const void *data_in,
+ const void **data_out);
+
+/*
+ * The dm_report_value_cache_{set,get} are helper functions to store and retrieve
+ * various values used during reporting (dm_report_field_type.report_fn) and/or
+ * selection processing (dm_report_reserved_handler instances) to avoid
+ * recalculation of these values or to share values among calls.
+ */
+int dm_report_value_cache_set(struct dm_report *rh, const char *name, const void *data);
+const void *dm_report_value_cache_get(struct dm_report *rh, const char *name);
+/*
* dm_report_init output_flags
*/
#define DM_REPORT_OUTPUT_MASK 0x000000FF
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index f8bd581..559d463 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -63,6 +63,7 @@ struct dm_report {
/* Null-terminated array of reserved values */
const struct dm_report_reserved_value *reserved_values;
+ struct dm_hash_table *value_cache;
};
/*
@@ -1222,6 +1223,8 @@ void dm_report_free(struct dm_report *rh)
{
if (rh->selection)
dm_pool_destroy(rh->selection->mem);
+ if (rh->value_cache)
+ dm_hash_destroy(rh->value_cache);
dm_pool_destroy(rh->mem);
dm_free(rh);
}
@@ -2205,6 +2208,21 @@ dm_percent_t dm_make_percent(uint64_t numerator, uint64_t denominator)
}
}
+int dm_report_value_cache_set(struct dm_report *rh, const char *name, const void *data)
+{
+ if (!rh->value_cache && (!(rh->value_cache = dm_hash_create(64)))) {
+ log_error("Failed to create cache for values used during reporting.");
+ return 0;
+ }
+
+ return dm_hash_insert(rh->value_cache, name, (void *) data);
+}
+
+const void *dm_report_value_cache_get(struct dm_report *rh, const char *name)
+{
+ return (rh->value_cache) ? dm_hash_lookup(rh->value_cache, name) : NULL;
+}
+
/*
* Used to check whether the reserved_values definition passed to
* dm_report_init_with_selection contains only supported reserved value types.
8 years, 9 months
master - cleanup: commit fe70b03 turned lv_time to STR, put it back to TIM
by Peter Rajnoha
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=82ecfa6f0eea0e...
Commit: 82ecfa6f0eea0e09dadc9b6e32f6d1359152997f
Parent: e944a9c63561f6a6bb5690f9df14ba4ababb02b3
Author: Peter Rajnoha <prajnoha(a)redhat.com>
AuthorDate: Fri Jul 3 09:22:48 2015 +0200
Committer: Peter Rajnoha <prajnoha(a)redhat.com>
CommitterDate: Fri Jul 3 09:22:48 2015 +0200
cleanup: commit fe70b03 turned lv_time to STR, put it back to TIM
---
lib/report/columns.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/report/columns.h b/lib/report/columns.h
index 22141d8..fe9aad9 100644
--- a/lib/report/columns.h
+++ b/lib/report/columns.h
@@ -85,7 +85,7 @@ FIELD(LVS, lv, STR, "Pool", lvid, 4, poollv, pool_lv, "For thin volumes, the thi
FIELD(LVS, lv, STR_LIST, "LV Tags", tags, 7, tags, lv_tags, "Tags, if any.", 0)
FIELD(LVS, lv, STR, "LProfile", lvid, 8, lvprofile, lv_profile, "Configuration profile attached to this LV.", 0)
FIELD(LVS, lv, STR, "Lock Args", lvid, 9, lvlockargs, lv_lockargs, "Lock args of the LV used by lvmlockd.", 0)
-FIELD(LVS, lv, STR, "Time", lvid, 26, lvtime, lv_time, "Creation time of the LV, if known", 0)
+FIELD(LVS, lv, TIM, "Time", lvid, 26, lvtime, lv_time, "Creation time of the LV, if known", 0)
FIELD(LVS, lv, STR, "Host", lvid, 10, lvhost, lv_host, "Creation host of the LV, if known.", 0)
FIELD(LVS, lv, STR_LIST, "Modules", lvid, 7, modules, lv_modules, "Kernel device-mapper modules required for this LV.", 0)
8 years, 9 months