1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00
lvm2/lib/report/report.c

4645 lines
143 KiB
C
Raw Normal View History

/*
2004-03-30 23:35:44 +04:00
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
vdo: add lvs fields to query vdo volume properties Add lots of vdo fields: vdo_operating_mode - For vdo pools, its current operating mode. vdo_compression_state - For vdo pools, whether compression is running. vdo_index_state - For vdo pools, state of index for deduplication. vdo_used_size - For vdo pools, currently used space. vdo_saving_percent - For vdo pools, percentage of saved space. vdo_compression - Set for compressed LV (vdopool). vdo_deduplication - Set for deduplicated LV (vdopool). vdo_use_metadata_hints - Use REQ_SYNC for writes (vdopool). vdo_minimum_io_size - Minimum acceptable IO size (vdopool). vdo_block_map_cache_size - Allocated caching size (vdopool). vdo_block_map_era_length - Speed of cache writes (vdopool). vdo_use_sparse_index - Sparse indexing (vdopool). vdo_index_memory_size - Allocated indexing memory (vdopool). vdo_slab_size - Increment size for growing (vdopool). vdo_ack_threads - Acknowledging threads (vdopool). vdo_bio_threads - IO submitting threads (vdopool). vdo_bio_rotation - IO enqueue (vdopool). vdo_cpu_threads - CPU threads for compression and hashing (vdopool). vdo_hash_zone_threads - Threads for subdivide parts (vdopool). vdo_logical_threads - Logical threads for subdivide parts (vdopool). vdo_physical_threads - Physical threads for subdivide parts (vdopool). vdo_max_discard - Maximum discard size volume can recieve (vdopool). vdo_write_policy - Specified write policy (vdopool). vdo_header_size - Header size at front of vdopool. Previously only 'lvdisplay -m' was exposing them.
2019-10-04 18:02:20 +03:00
* Copyright (C) 2004-2019 Red Hat, Inc. All rights reserved.
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2.
*
2004-03-30 23:35:44 +04:00
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/report/report.h"
#include "lib/commands/toolcontext.h"
#include "lib/misc/lvm-string.h"
#include "lib/display/display.h"
#include "lib/activate/activate.h"
#include "lib/metadata/segtype.h"
#include "lib/cache/lvmcache.h"
#include "lib/device/device-types.h"
#include "lib/device/device_id.h"
#include "lib/datastruct/str_list.h"
#include "lib/locking/lvmlockd.h"
#include <stddef.h> /* offsetof() */
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
#include <float.h> /* DBL_MAX */
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.
2015-07-03 11:43:07 +03:00
#include <time.h>
/*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* IMPORTANT NOTE ABOUT ADDING A NEW VALUE FOR REPORTING
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* When adding a new string value to report, try to keep it
* self-descriptive so when it's printed even without the header,
* we can still deduce what it is actually reporting.
*
* If you need more than one descriptive string to mean the same value,
* please define them as reserved values in values.h.
*
* The first reserved value is the one that is printed in reports (unless
* it's a binary value and we have report/binary_values_as_numeric=1 config
* option used OR --binary command line option is used OR we're using an
* output format which must always print binary values in numeric way,
* like json_std output format.
*
* All the other (2nd and further) listed reserved names are synonyms which
* may be also used in selection (-S|--select).
*
* Also, always use proper *_disp functions to display each type of value
* properly. For example, in case of binary values, you should use
* _binary_disp so that we can always switch between numerical (0/1/-1) and
* string representation while reporting the value.
*/
struct lvm_report_object {
struct volume_group *vg;
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
struct lv_with_info_and_seg_status *lvdm;
struct physical_volume *pv;
struct lv_segment *seg;
struct pv_segment *pvseg;
struct label *label;
};
static uint32_t _log_seqnum = 1;
/*
* Enum for field_num index to use in per-field reserved value definition.
* Each field is represented by enum value with name "field_<id>" where <id>
* is the field_id of the field as registered in columns.h.
*/
#define FIELD(type, strct, sorttype, head, field_name, width, func, id, desc, writeable) field_ ## id,
enum {
/* coverity[unnecessary_header] */
#include "columns.h"
};
#undef FIELD
static const uint64_t _zero64 = UINT64_C(0);
2014-07-02 13:09:14 +04:00
static const uint64_t _one64 = UINT64_C(1);
static const uint64_t _two64 = UINT64_C(2);
static const char _str_zero[] = "0";
static const char _str_one[] = "1";
static const char _str_no[] = "no";
static const char _str_yes[] = "yes";
static const char _str_unknown[] = "unknown";
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
static const double _siz_max = DBL_MAX;
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
/*
* 32 bit signed is casted to 64 bit unsigned in dm_report_field internally!
* So when stored in the struct, the _reserved_num_undef_32 is actually
* equal to _reserved_num_undef_64.
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
*/
static const int32_t _reserved_num_undef_32 = INT32_C(-1);
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
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.
2015-07-03 11:43:07 +03:00
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;
};
2017-07-19 17:17:30 +03:00
#define ADD_TIME_PROP(id, flags, granularity) [(id)] = {(id), (flags), (granularity)},
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.
2015-07-03 11:43:07 +03:00
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[16];
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.
2015-07-03 11:43:07 +03:00
const struct time_prop *prop;
uint32_t reg_flags;
};
2017-07-19 17:17:30 +03:00
#define TIME_PROP(id) (_time_props + (id))
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.
2015-07-03 11:43:07 +03:00
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},
};
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));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
/*
static int _is_time_frame(time_id_t id)
{
return ((id > TIME_FRAME__START) && (id < TIME_FRAME__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
*/
static int _is_time_label_date(time_id_t id)
{
return ((id > TIME_LABEL_DATE__START) && (id < TIME_LABEL_DATE__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
static int _is_time_label_time(time_id_t id)
{
return ((id > TIME_LABEL_TIME__START) && (id < TIME_LABEL_TIME__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
static int _is_time_unit(time_id_t id)
{
return ((id > TIME_UNIT__START) && (id < TIME_UNIT__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
static int _is_time_weekday(time_id_t id)
{
return ((id > TIME_WEEKDAY__START) && (id < TIME_WEEKDAY__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
static int _is_time_month(time_id_t id)
{
return ((id > TIME_MONTH__START) && (id < TIME_MONTH__END));
2020-08-28 22:14:06 +03:00
}
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.
2015-07-03 11:43:07 +03:00
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; i < DM_ARRAY_SIZE(_time_reg); ++i) {
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.
2015-07-03 11:43:07 +03:00
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;
}
if (_is_time_label_date(ti->prop->id))
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.
2015-07-03 11:43:07 +03:00
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;
}
if (_is_time_label_time(ti->prop->id))
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.
2015-07-03 11:43:07 +03:00
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 ((time_t)86400)
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.
2015-07-03 11:43:07 +03:00
static const int _days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
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.
2015-07-03 11:43:07 +03:00
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;
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)) {
errno = 0;
num = strtol(ti->s, NULL, 10);
if (errno) {
log_error("_translate_time_items: invalid time.");
return 0;
}
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.
2015-07-03 11:43:07 +03:00
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 && ti_p->prop->id == TIME_NUM_MULTIPLIER)
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.
2015-07-03 11:43:07 +03:00
tm.tm_hour = multiplier;
break;
case TIME_UNIT_PM:
if (ti_p && _is_time_unit(ti_p->prop->id))
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.
2015-07-03 11:43:07 +03:00
t -= 12 * SECS_PER_HOUR;
else if (ti_p && ti_p->prop->id == TIME_NUM_MULTIPLIER)
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.
2015-07-03 11:43:07 +03:00
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;
if (dm_snprintf(buf, sizeof(buf), "@" FMTd64 ":@" FMTd64, (int64_t)t1, (int64_t)t2) == -1) {
2015-07-09 16:15:15 +03:00
log_error("_translate_time_items: dm_snprintf failed");
return 0;
}
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.
2015-07-03 11:43:07 +03:00
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)
{
int64_t t1, t2;
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.
2015-07-03 11:43:07 +03:00
time_t *result;
if (sscanf(data_in, "@" FMTd64 ":@" FMTd64, &t1, &t2) != 2) {
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.
2015-07-03 11:43:07 +03:00
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] = (time_t) t1; /* Validate range for 32b arch ? */
result[1] = (time_t) t2;
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.
2015-07-03 11:43:07 +03:00
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)
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.
2015-07-03 11:43:07 +03:00
{
*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.
*/
#define GET_TYPE_RESERVED_VALUE(id) _reserved_ ## id
/*
* Get field reserved value - the value returned is always a pointer (const void *).
*/
#define GET_FIELD_RESERVED_VALUE(id) _reserved_ ## id.value
/*
* Get first name assigned to the reserved value - this is the one that
* should be reported/displayed. All the other names assigned for the reserved
* value are synonyms recognized in selection criteria.
*/
#define GET_FIRST_RESERVED_NAME(id) _reserved_ ## id ## _names[0]
/*
* Reserved values and their assigned names.
* The first name is the one that is also used for reporting.
* All names listed are synonyms recognized in selection criteria.
* For binary-based values we map all reserved names listed onto value 1, blank onto value 0.
*
* TYPE_RESERVED_VALUE(type, reserved_value_id, description, value, reserved name, ...)
* FIELD_RESERVED_VALUE(field_id, reserved_value_id, description, value, reserved name, ...)
* FIELD_RESERVED_BINARY_VALUE(field_id, reserved_value_id, description, reserved name for 1, ...)
*
* Note: FIELD_RESERVED_BINARY_VALUE creates:
* - 'reserved_value_id_y' (for 1)
* - 'reserved_value_id_n' (for 0)
*/
2014-07-10 13:54:37 +04:00
#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
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.
2015-05-19 14:01:48 +03:00
#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
2014-07-10 13:54:37 +04:00
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) \
2024-05-03 13:24:02 +03:00
static const char * const _reserved_ ## id ## _names[] = { __VA_ARGS__, NULL}; \
static const type _reserved_ ## id = value;
2014-07-10 13:54:37 +04:00
#define FIELD_RESERVED_VALUE(flags, field_id, id, desc, value, ...) \
2024-05-03 13:24:02 +03:00
static const char * const _reserved_ ## id ## _names[] = { __VA_ARGS__ , NULL}; \
static const struct dm_report_field_reserved_value _reserved_ ## id = {field_ ## field_id, value};
2014-07-10 13:54:37 +04:00
#define FIELD_RESERVED_BINARY_VALUE(field_id, id, desc, ...) \
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _y, desc, &_one64, __VA_ARGS__, _str_yes) \
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _n, desc, &_zero64, __VA_ARGS__, _str_no)
#include "values.h"
2014-07-10 13:54:37 +04:00
#undef NUM
#undef NUM_HND
#undef HND
#undef NOFLAG
#undef NAMED
#undef RANGE
#undef TYPE_RESERVED_VALUE
#undef FIELD_RESERVED_VALUE
#undef FIELD_RESERVED_BINARY_VALUE
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.
2015-05-19 14:01:48 +03:00
#undef FUZZY
#undef DYNAMIC
/*
* Create array of reserved values to be registered with reporting code via
* dm_report_init_with_selection function that initializes report with
* selection criteria. Selection code then recognizes these reserved values
* when parsing selection criteria.
*/
2014-07-10 13:54:37 +04:00
#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
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.
2015-05-19 14:01:48 +03:00
#define FUZZY DM_REPORT_FIELD_RESERVED_VALUE_FUZZY_NAMES
#define DYNAMIC DM_REPORT_FIELD_RESERVED_VALUE_DYNAMIC_VALUE
2014-07-10 13:54:37 +04:00
2024-05-03 13:24:02 +03:00
/* Declaration of dm_report_reserved_value should actually be using const char * const * names */
#define TYPE_RESERVED_VALUE(type, flags, id, desc, value, ...) {type | flags, &_reserved_ ## id, (const char**) _reserved_ ## id ## _names, desc},
2014-07-10 13:54:37 +04:00
2024-05-03 13:24:02 +03:00
#define FIELD_RESERVED_VALUE(flags, field_id, id, desc, value, ...) {DM_REPORT_FIELD_TYPE_NONE | flags, &_reserved_ ## id, (const char**) _reserved_ ## id ## _names, desc},
2014-07-10 13:54:37 +04:00
#define FIELD_RESERVED_BINARY_VALUE(field_id, id, desc, ...) \
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _y, desc, &_one64, __VA_ARGS__) \
FIELD_RESERVED_VALUE(NAMED, field_id, id ## _n, desc, &_zero64, __VA_ARGS__)
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
static const struct dm_report_reserved_value _report_reserved_values[] = {
#include "values.h"
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
{0, NULL, NULL, NULL}
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
};
2014-07-10 13:54:37 +04:00
#undef NUM
#undef NUM_HND
#undef HND
#undef NOFLAG
#undef NAMED
#undef RANGE
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.
2015-05-19 14:01:48 +03:00
#undef FUZZY
#undef DYNAMIC
#undef TYPE_RESERVED_VALUE
#undef FIELD_RESERVED_VALUE
#undef FIELD_RESERVED_BINARY_VALUE
static int _field_string(struct dm_report *rh, struct dm_report_field *field, const char *data)
{
return dm_report_field_string(rh, field, &data);
}
static int _field_set_value(struct dm_report_field *field, const void *data, const void *sort)
{
dm_report_field_set_value(field, data, sort);
return 1;
}
static int _field_set_string_list(struct dm_report *rh, struct dm_report_field *field,
const struct dm_list *list, void *private, int sorted,
const char *delimiter)
{
struct cmd_context *cmd = (struct cmd_context *) private;
return sorted ? dm_report_field_string_list(rh, field, list, delimiter ? : cmd->report_list_item_separator)
: dm_report_field_string_list_unsorted(rh, field, list, delimiter ? : cmd->report_list_item_separator);
}
/*
* Data-munging functions to prepare each data type for display and sorting
*/
/*
* Display either "0"/"1" or ""/"word" based on bin_value,
* cmd->report_binary_values_as_numeric selects the mode to use.
*/
static int _binary_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, int bin_value, const char *word,
void *private)
{
const struct cmd_context *cmd = (const struct cmd_context *) private;
if (cmd->report_strict_type_mode || cmd->report_binary_values_as_numeric)
/* "0"/"1" */
return _field_set_value(field, bin_value ? _str_one : _str_zero, bin_value ? &_one64 : &_zero64);
/* blank/"word" */
return _field_set_value(field, bin_value ? word : "", bin_value ? &_one64 : &_zero64);
}
static int _binary_undef_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field, void *private)
{
const struct cmd_context *cmd = (const struct cmd_context *) private;
if (cmd->report_strict_type_mode || cmd->report_binary_values_as_numeric)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(num_undef_64), &GET_TYPE_RESERVED_VALUE(num_undef_64));
return _field_set_value(field, _str_unknown, &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _string_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return dm_report_field_string(rh, field, (const char * const *) data);
}
static int _chars_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _field_string(rh, field, data);
}
static int _uuid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
char *repstr;
if (!(repstr = id_format_and_copy(mem, data)))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _devminor_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int devminor = (int) MINOR((*(const struct device * const *) data)->dev);
return dm_report_field_int(rh, field, &devminor);
}
static int _devmajor_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int devmajor = (int) MAJOR((*(const struct device * const *) data)->dev);
return dm_report_field_int(rh, field, &devmajor);
}
static int _dev_name_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _field_string(rh, field, dev_name(*(const struct device * const *) data));
}
static int _devices_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct dm_list *list;
if (!(list = lvseg_devices(mem, seg)))
return_0;
return _field_set_string_list(rh, field, list, private, 0, ",");
}
static int _metadatadevices_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct dm_list *list;
if (!(list = lvseg_metadata_devices(mem, seg)))
return_0;
return _field_set_string_list(rh, field, list, private, 0, ",");
}
static int _peranges_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct dm_list *list;
if (!(list = lvseg_seg_pe_ranges(mem, seg)))
return_0;
return _field_set_string_list(rh, field, list, private, 0, " ");
}
2016-01-19 15:51:11 +03:00
static int _leranges_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct dm_list *list;
if (!(list = lvseg_seg_le_ranges(mem, seg)))
return_0;
return _field_set_string_list(rh, field, list, private, 0, NULL);
}
static int _metadataleranges_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct dm_list *list;
if (!(list = lvseg_seg_metadata_le_ranges(mem, seg)))
return_0;
report: make devices, metadata_devices, seg_pe_ranges and seg_metadata_le_ranges fields consistent There are two basic groups of fields for LV segment device reporting: - related to LV segment's devices: devices and seg_pe_ranges - related to LV segment's metadata devices: metadata_devices and seg_metadata_le_ranges The devices and metadata_devices report devices in this format: "device_name(extent_start)" The seg_pe_ranges and seg_metadata_le_ranges report devices in this format: "device_name:extent_start-extent_end" This patch reverts partly what commit 7f74a995029caa41ee3cf9aec0bd024a34bfd89a (v 2.02.140) introduced in this area - it added [] for hidden devices to mark them for all four fields mentioned above. We won't be marking hidden devices in devices and metadata_devices fields. The seg_metadata_le_ranges field will have hidden devices marked - it's new enough that we don't need to care about compatibility much yet. The seg_pe_ranges is old enough that we shouldn't be changing this one - so we're reverting to not marking hidden devices here. Instead, there's going to be a new field "seg_le_ranges" which is going to replace the seg_pe_ranges and it will mark hidden devices - this is going to be introduced in a patch later. So in the end we'll end up with: (LV segment's devices) devices field with "device_name(extent_start)" format, not marking hidden devices seg_pe_ranges field with "device_name:extent_start-extent_end" format, not marking hidden devices (deprecated, new seg_le_ranges should be used instead for standardized format) seg_le_ranges field with "device_name:extent_start-extent_end" format, marking hidden devices (LV segment's metadata devices) metadata_devices field with "device_name:extent_start-extent_end" format, not marking hidden devices seg_metadata_le_ranges field with "device_name:extent_start-extent_end" format, marking hidden devices Also, both seg_le_ranges and seg_metadata_le_ranges will honour the report/list_item_separator setting which can be used to configure the delimiter used for list items. So, to sum it up, we will recommend using the new seg_le_ranges and seg_metadata_le_ranges fields because they display devices with standard extent range format, they can mark hidden devices and they honour the report/list_item_separator setting. We'll be keeping devices,seg_pe_ranges and metadata_devices fields for compatibility.
2016-01-19 14:26:01 +03:00
return _field_set_string_list(rh, field, list, private, 0, NULL);
}
static int _tags_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
2004-03-08 20:19:15 +03:00
{
const struct dm_list *tagsl = (const struct dm_list *) data;
2004-03-08 20:19:15 +03:00
return _field_set_string_list(rh, field, tagsl, private, 1, NULL);
2004-03-08 20:19:15 +03:00
}
struct _str_list_append_baton {
struct dm_pool *mem;
struct dm_list *result;
};
static int _str_list_append(const char *line, void *baton)
{
struct _str_list_append_baton *b = baton;
const char *line2 = dm_pool_strdup(b->mem, line);
if (!line2)
return_0;
if (!str_list_add(b->mem, b->result, line2))
return_0;
return 1;
}
static int _cache_settings_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
const struct lv_segment *setting_seg = NULL;
const struct dm_config_node *settings;
struct dm_list *result;
struct _str_list_append_baton baton;
struct dm_list dummy_list; /* dummy list to display "nothing" */
if (seg_is_writecache(seg)) {
if (!(result = str_list_create(mem)))
return_0;
if (!writecache_settings_to_str_list((struct writecache_settings *)&seg->writecache_settings, result, mem))
return_0;
return _field_set_string_list(rh, field, result, private, 0, NULL);
}
if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv))
Allow dm-cache cache device to be standard LV If a single, standard LV is specified as the cache, use it directly instead of converting it into a cache-pool object with two separate LVs (for data and metadata). With a single LV as the cache, lvm will use blocks at the beginning for metadata, and the rest for data. Separate dm linear devices are set up to point at the metadata and data areas of the LV. These dm devs are given to the dm-cache target to use. The single LV cache cannot be resized without recreating it. If the --poolmetadata option is used to specify an LV for metadata, then a cache pool will be created (with separate LVs for data and metadata.) Usage: $ lvcreate -n main -L 128M vg /dev/loop0 $ lvcreate -n fast -L 64M vg /dev/loop1 $ lvs -a vg LV VG Attr LSize Type Devices main vg -wi-a----- 128.00m linear /dev/loop0(0) fast vg -wi-a----- 64.00m linear /dev/loop1(0) $ lvconvert --type cache --cachepool fast vg/main $ lvs -a vg LV VG Attr LSize Origin Pool Type Devices [fast] vg Cwi---C--- 64.00m linear /dev/loop1(0) main vg Cwi---C--- 128.00m [main_corig] [fast] cache main_corig(0) [main_corig] vg owi---C--- 128.00m linear /dev/loop0(0) $ lvchange -ay vg/main $ dmsetup ls vg-fast_cdata (253:4) vg-fast_cmeta (253:5) vg-main_corig (253:6) vg-main (253:24) vg-fast (253:3) $ dmsetup table vg-fast_cdata: 0 98304 linear 253:3 32768 vg-fast_cmeta: 0 32768 linear 253:3 0 vg-main_corig: 0 262144 linear 7:0 2048 vg-main: 0 262144 cache 253:5 253:4 253:6 128 2 metadata2 writethrough mq 0 vg-fast: 0 131072 linear 7:1 2048 $ lvchange -an vg/min $ lvconvert --splitcache vg/main $ lvs -a vg LV VG Attr LSize Type Devices fast vg -wi------- 64.00m linear /dev/loop1(0) main vg -wi------- 128.00m linear /dev/loop0(0)
2018-08-17 23:45:52 +03:00
setting_seg = seg;
else if (seg_is_cache_pool(seg))
setting_seg = seg;
else if (seg_is_cache(seg))
setting_seg = first_seg(seg->pool_lv);
if (!setting_seg || !setting_seg->policy_settings) {
dm_list_init(&dummy_list);
return _field_set_string_list(rh, field, &dummy_list, private, 0, NULL);
/* TODO: once we have support for STR_LIST reserved values, replace with:
* return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_settings_undef), GET_FIELD_RESERVED_VALUE(cache_settings_undef));
*/
}
settings = setting_seg->policy_settings->child;
if (!(result = str_list_create(mem)))
return_0;
baton.mem = mem;
baton.result = result;
while (settings) {
dm_config_write_one_node(settings, _str_list_append, &baton);
settings = settings->sib;
};
return _field_set_string_list(rh, field, result, private, 0, NULL);
}
static int _do_get_kernel_cache_settings_list(struct dm_pool *mem,
int cache_argc, char **cache_argv,
struct dm_list *result)
{
const char *key, *value;
char *buf;
size_t buf_len;
int i;
for (i = 0; i+1 < cache_argc; i += 2) {
key = cache_argv[i];
value = cache_argv[i+1];
/* +1 for "=" char and +1 for trailing zero */
buf_len = strlen(key) + strlen(value) + 2;
if (!(buf = dm_pool_alloc(mem, buf_len)))
return_0;
if (dm_snprintf(buf, buf_len, "%s=%s", key, value) < 0)
return_0;
if (!str_list_add_no_dup_check(mem, result, buf))
return_0;
}
return 1;
}
static int _get_kernel_cache_settings_list(struct dm_pool *mem,
struct dm_status_cache *cache_status,
struct dm_list **result)
{
if (!(*result = str_list_create(mem)))
return_0;
if (!_do_get_kernel_cache_settings_list(mem, cache_status->core_argc,
cache_status->core_argv, *result))
return_0;
if (!_do_get_kernel_cache_settings_list(mem, cache_status->policy_argc,
cache_status->policy_argv, *result))
return_0;
return 1;
}
static int _kernel_cache_settings_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
struct dm_list dummy_list; /* dummy list to display "nothing" */
struct dm_list *result;
int r = 0;
if (lvdm->seg_status.type != SEG_STATUS_CACHE) {
dm_list_init(&dummy_list);
return _field_set_string_list(rh, field, &dummy_list, private, 0, NULL);
}
if (!(mem = dm_pool_create("reporter_pool", 1024)))
return_0;
if (!_get_kernel_cache_settings_list(mem, lvdm->seg_status.cache, &result))
goto_out;
r = _field_set_string_list(rh, field, result, private, 0, NULL);
out:
dm_pool_destroy(mem);
return r;
}
static int _kernel_cache_policy_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lvdm->seg_status.type == SEG_STATUS_CACHE) &&
lvdm->seg_status.cache->policy_name)
return _field_string(rh, field, lvdm->seg_status.cache->policy_name);
return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_policy_undef),
GET_FIELD_RESERVED_VALUE(cache_policy_undef));
}
static int _kernelmetadataformat_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
unsigned format;
if (lvdm->seg_status.type == SEG_STATUS_CACHE) {
format = (lvdm->seg_status.cache->feature_flags & DM_CACHE_FEATURE_METADATA2);
return dm_report_field_uint64(rh, field, format ? &_two64 : &_one64);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _cache_policy_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
const struct lv_segment *setting_seg = NULL;
if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv))
Allow dm-cache cache device to be standard LV If a single, standard LV is specified as the cache, use it directly instead of converting it into a cache-pool object with two separate LVs (for data and metadata). With a single LV as the cache, lvm will use blocks at the beginning for metadata, and the rest for data. Separate dm linear devices are set up to point at the metadata and data areas of the LV. These dm devs are given to the dm-cache target to use. The single LV cache cannot be resized without recreating it. If the --poolmetadata option is used to specify an LV for metadata, then a cache pool will be created (with separate LVs for data and metadata.) Usage: $ lvcreate -n main -L 128M vg /dev/loop0 $ lvcreate -n fast -L 64M vg /dev/loop1 $ lvs -a vg LV VG Attr LSize Type Devices main vg -wi-a----- 128.00m linear /dev/loop0(0) fast vg -wi-a----- 64.00m linear /dev/loop1(0) $ lvconvert --type cache --cachepool fast vg/main $ lvs -a vg LV VG Attr LSize Origin Pool Type Devices [fast] vg Cwi---C--- 64.00m linear /dev/loop1(0) main vg Cwi---C--- 128.00m [main_corig] [fast] cache main_corig(0) [main_corig] vg owi---C--- 128.00m linear /dev/loop0(0) $ lvchange -ay vg/main $ dmsetup ls vg-fast_cdata (253:4) vg-fast_cmeta (253:5) vg-main_corig (253:6) vg-main (253:24) vg-fast (253:3) $ dmsetup table vg-fast_cdata: 0 98304 linear 253:3 32768 vg-fast_cmeta: 0 32768 linear 253:3 0 vg-main_corig: 0 262144 linear 7:0 2048 vg-main: 0 262144 cache 253:5 253:4 253:6 128 2 metadata2 writethrough mq 0 vg-fast: 0 131072 linear 7:1 2048 $ lvchange -an vg/min $ lvconvert --splitcache vg/main $ lvs -a vg LV VG Attr LSize Type Devices fast vg -wi------- 64.00m linear /dev/loop1(0) main vg -wi------- 128.00m linear /dev/loop0(0)
2018-08-17 23:45:52 +03:00
setting_seg = seg;
else if (seg_is_cache_pool(seg))
setting_seg = seg;
else if (seg_is_cache(seg))
setting_seg = first_seg(seg->pool_lv);
if (!setting_seg || !setting_seg->policy_name)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(cache_policy_undef),
GET_FIELD_RESERVED_VALUE(cache_policy_undef));
return _field_string(rh, field, setting_seg->policy_name);
}
static int _modules_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct dm_list *modules;
if (!(modules = str_list_create(mem))) {
log_error("modules str_list allocation failed");
return 0;
}
if (!(list_lv_modules(mem, lv, modules)))
return_0;
return _field_set_string_list(rh, field, modules, private, 1, NULL);
}
static int _lvprofile_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv->profile)
return _field_string(rh, field, lv->profile->name);
return _field_set_value(field, "", NULL);
}
2015-03-05 23:00:44 +03:00
static int _lvlockargs_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
return _field_string(rh, field, lv->lock_args ? : "");
2015-03-05 23:00:44 +03:00
}
static int _vgfmt_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
if (vg->fid && vg->fid->fmt)
return _field_string(rh, field, vg->fid->fmt->name);
return _field_set_value(field, "", NULL);
}
static int _pvfmt_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct label *l = (const struct label *) data;
if (l->labeller && l->labeller->fmt)
return _field_string(rh, field, l->labeller->fmt->name);
return _field_set_value(field, "", NULL);
}
static int _lvkmaj_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if (lvdm->info.exists && lvdm->info.major >= 0)
return dm_report_field_int(rh, field, &lvdm->info.major);
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
}
static int _lvkmin_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if (lvdm->info.exists && lvdm->info.minor >= 0)
return dm_report_field_int(rh, field, &lvdm->info.minor);
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
}
static int _lvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
char *repstr;
if (!(repstr = lv_attr_dup_with_info_and_seg_status(mem, lvdm)))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _pvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
char *repstr;
if (!(repstr = pv_attr_dup(mem, pv)))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _vgstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct volume_group *vg = (const struct volume_group *) data;
char *repstr;
if (!(repstr = vg_attr_dup(mem, vg)))
return_0;
2005-08-16 03:34:11 +04:00
return _field_set_value(field, repstr, NULL);
}
static int _segtype_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct lv_segment *seg = (const struct lv_segment *) data;
char *name;
if (!(name = lvseg_segtype_dup(mem, seg))) {
log_error("Failed to get segtype name.");
return 0;
}
return _field_set_value(field, name, NULL);
}
static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
const struct logical_volume *lv = (const struct logical_volume *) data;
int is_historical = lv_is_historical(lv);
const char *tmp_lvname;
char *repstr, *lvname;
size_t len;
if (!is_historical && (lv_is_visible(lv) || !cmd->report_mark_hidden_devices))
return _field_string(rh, field, lv->name);
if (is_historical) {
tmp_lvname = lv->this_glv->historical->name;
len = strlen(tmp_lvname) + strlen(HISTORICAL_LV_PREFIX) + 1;
} else {
tmp_lvname = lv->name;
len = strlen(tmp_lvname) + 3;
}
if (!(repstr = dm_pool_zalloc(mem, len))) {
log_error("dm_pool_alloc failed");
return 0;
}
if (dm_snprintf(repstr, len, "%s%s%s",
is_historical ? HISTORICAL_LV_PREFIX : "[",
tmp_lvname,
is_historical ? "" : "]") < 0) {
log_error("lvname snprintf failed");
return 0;
}
if (!(lvname = dm_pool_strdup(mem, tmp_lvname))) {
log_error("dm_pool_strdup failed");
return 0;
}
return _field_set_value(field, repstr, lvname);
}
static int _do_loglv_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private,
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *mirror_log_lv = lv_mirror_log_lv(lv);
if (!mirror_log_lv)
return _field_set_value(field, "", NULL);
if (uuid)
return _uuid_disp(rh, mem, field, &mirror_log_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, mirror_log_lv, private);
}
static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_loglv_disp(rh, mem, field, data, private, 0);
}
static int _loglvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_loglv_disp(rh, mem, field, data, private, 1);
}
static int _lvfullname_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
if (!(repstr = lv_fullname_dup(mem, lv)))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _lvparent_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *parent_lv = lv_parent(lv);
if (!parent_lv)
return _field_set_value(field, "", NULL);
return _lvname_disp(rh, mem, field, parent_lv, private);
}
2015-09-21 14:07:28 +03:00
static int _do_datalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)),
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *data_lv = lv_data_lv(lv);
if (!data_lv)
return _field_set_value(field, "", NULL);
if (uuid)
return _uuid_disp(rh, mem, field, &data_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, data_lv, private);
}
2015-09-21 14:07:28 +03:00
static int _datalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_datalv_disp(rh, mem, field, data, private, 0);
}
static int _datalvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_datalv_disp(rh, mem, field, data, private, 1);
}
2015-09-21 13:59:08 +03:00
static int _do_metadatalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)),
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *metadata_lv = lv_metadata_lv(lv);
if (!metadata_lv)
return _field_set_value(field, "", NULL);
if (uuid)
return _uuid_disp(rh, mem, field, &metadata_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, metadata_lv, private);
}
2015-09-21 13:59:08 +03:00
static int _metadatalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_metadatalv_disp(rh, mem, field, data, private, 0);
}
static int _metadatalvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_metadatalv_disp(rh, mem, field, data, private, 1);
}
2015-09-21 13:28:58 +03:00
static int _do_poollv_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private,
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *pool_lv = lv_pool_lv(lv);
if (!pool_lv)
return _field_set_value(field, "", NULL);
if (uuid)
return _uuid_disp(rh, mem, field, &pool_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, pool_lv, private);
}
2015-09-21 13:28:58 +03:00
static int _poollv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_poollv_disp(rh, mem, field, data, private, 0);
}
static int _poollvuuid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_poollv_disp(rh, mem, field, data, private, 1);
}
static int _lvpath_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
if (!(repstr = lv_path_dup(mem, lv)))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _lvdmpath_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
if (!(repstr = lv_dmpath_dup(mem, lv)))
return_0;
return _field_set_value(field, repstr, NULL);
}
2015-09-21 13:44:29 +03:00
static int _do_origin_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private,
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct logical_volume *origin_lv = lv_origin_lv(lv);
if (!origin_lv)
2015-09-21 13:44:29 +03:00
return _field_set_value(field, "", NULL);
2015-09-21 13:44:29 +03:00
if (uuid)
return _uuid_disp(rh, mem, field, &origin_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, origin_lv, private);
2015-09-21 13:44:29 +03:00
}
2015-09-21 13:44:29 +03:00
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _do_origin_disp(rh, mem, field, data, private, 0);
}
2015-09-21 13:44:29 +03:00
static int _originuuid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _do_origin_disp(rh, mem, field, data, private, 1);
}
static const char *_get_glv_str(char *buf, size_t buf_len,
struct generic_logical_volume *glv)
{
if (!glv->is_historical)
return glv->live->name;
if (dm_snprintf(buf, buf_len, "%s%s", HISTORICAL_LV_PREFIX, glv->historical->name) < 0) {
log_error("_get_glv_str: dm_snprintf failed");
return NULL;
}
return buf;
}
static int _find_ancestors(struct _str_list_append_baton *ancestors,
struct generic_logical_volume glv,
int full, int include_historical_lvs)
{
struct lv_segment *seg;
void *orig_p = glv.live;
const char *ancestor_str;
char buf[NAME_LEN + sizeof(HISTORICAL_LV_PREFIX)];
if (glv.is_historical) {
if (full && glv.historical->indirect_origin)
glv = *glv.historical->indirect_origin;
} else if (lv_is_cow(glv.live)) {
glv.live = origin_from_cow(glv.live);
} else if (lv_is_thin_volume(glv.live)) {
seg = first_seg(glv.live);
if (seg->origin)
glv.live = seg->origin;
else if (seg->external_lv)
glv.live = seg->external_lv;
else if (full && seg->indirect_origin)
glv = *seg->indirect_origin;
}
if (orig_p != glv.live) {
if (!(ancestor_str = _get_glv_str(buf, sizeof(buf), &glv)))
return_0;
if (!glv.is_historical || include_historical_lvs) {
if (!_str_list_append(ancestor_str, ancestors))
return_0;
}
if (!_find_ancestors(ancestors, glv, full, include_historical_lvs))
return_0;
}
return 1;
}
static int _lvancestors_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
struct logical_volume *lv = (struct logical_volume *) data;
struct _str_list_append_baton ancestors;
struct generic_logical_volume glv;
ancestors.mem = mem;
if (!(ancestors.result = str_list_create(mem)))
return_0;
if ((glv.is_historical = lv_is_historical(lv)))
glv.historical = lv->this_glv->historical;
else
glv.live = lv;
if (!_find_ancestors(&ancestors, glv, 0, cmd->include_historical_lvs)) {
dm_pool_free(ancestors.mem, ancestors.result);
return_0;
}
return _field_set_string_list(rh, field, ancestors.result, private, 0, NULL);
}
static int _lvfullancestors_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
struct logical_volume *lv = (struct logical_volume *) data;
struct _str_list_append_baton full_ancestors;
struct generic_logical_volume glv;
full_ancestors.mem = mem;
if (!(full_ancestors.result = str_list_create(mem)))
return_0;
if ((glv.is_historical = lv_is_historical(lv)))
glv.historical = lv->this_glv->historical;
else
glv.live = lv;
if (!_find_ancestors(&full_ancestors, glv, 1, cmd->include_historical_lvs)) {
dm_pool_free(full_ancestors.mem, full_ancestors.result);
return_0;
}
return _field_set_string_list(rh, field, full_ancestors.result, private, 0, NULL);
}
static int _find_descendants(struct _str_list_append_baton *descendants,
struct generic_logical_volume glv,
int full, int include_historical_lvs)
{
struct generic_logical_volume glv_next = {0};
const struct seg_list *sl;
struct lv_segment *seg;
struct glv_list *glvl;
struct dm_list *list;
const char *descendant_str;
char buf[64];
if (glv.is_historical) {
if (full) {
list = &glv.historical->indirect_glvs;
dm_list_iterate_items(glvl, list) {
if (!glvl->glv->is_historical || include_historical_lvs) {
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), glvl->glv)))
return_0;
if (!_str_list_append(descendant_str, descendants))
return_0;
}
if (!_find_descendants(descendants, *glvl->glv, full, include_historical_lvs))
return_0;
}
}
} else if (lv_is_origin(glv.live)) {
list = &glv.live->snapshot_segs;
dm_list_iterate_items_gen(seg, list, origin_list) {
if ((glv.live = seg->cow)) {
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), &glv)))
return_0;
if (!_str_list_append(descendant_str, descendants))
return_0;
if (!_find_descendants(descendants, glv, full, include_historical_lvs))
return_0;
}
}
} else {
list = &glv.live->segs_using_this_lv;
dm_list_iterate_items(sl, list) {
if (lv_is_thin_volume(sl->seg->lv)) {
seg = first_seg(sl->seg->lv);
if ((seg->origin == glv.live) || (seg->external_lv == glv.live)) {
glv_next.live = sl->seg->lv;
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), &glv_next)))
return_0;
if (!_str_list_append(descendant_str, descendants))
return_0;
if (!_find_descendants(descendants, glv_next, full, include_historical_lvs))
return_0;
}
}
}
if (full) {
list = &glv.live->indirect_glvs;
dm_list_iterate_items(glvl, list) {
if (!glvl->glv->is_historical || include_historical_lvs) {
if (!(descendant_str = _get_glv_str(buf, sizeof(buf), glvl->glv)))
return_0;
if (!_str_list_append(descendant_str, descendants))
return_0;
}
if (!_find_descendants(descendants, *glvl->glv, full, include_historical_lvs))
return_0;
}
}
}
return 1;
}
static int _lvdescendants_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
struct logical_volume *lv = (struct logical_volume *) data;
struct _str_list_append_baton descendants;
struct generic_logical_volume glv;
descendants.mem = mem;
if (!(descendants.result = str_list_create(mem)))
return_0;
if ((glv.is_historical = lv_is_historical(lv)))
glv.historical = lv->this_glv->historical;
else
glv.live = lv;
if (!_find_descendants(&descendants, glv, 0, cmd->include_historical_lvs)) {
dm_pool_free(descendants.mem, descendants.result);
return_0;
}
return _field_set_string_list(rh, field, descendants.result, private, 0, NULL);
}
static int _lvfulldescendants_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
struct logical_volume *lv = (struct logical_volume *) data;
struct _str_list_append_baton descendants;
struct generic_logical_volume glv;
descendants.mem = mem;
if (!(descendants.result = str_list_create(mem)))
return_0;
if ((glv.is_historical = lv_is_historical(lv)))
glv.historical = lv->this_glv->historical;
else
glv.live = lv;
if (!_find_descendants(&descendants, glv, 1, cmd->include_historical_lvs)) {
dm_pool_free(descendants.mem, descendants.result);
return_0;
}
return _field_set_string_list(rh, field, descendants.result, private, 0, NULL);
}
static int _do_movepv_disp(struct dm_report *rh, struct dm_pool *mem,
2015-09-21 15:01:41 +03:00
struct dm_report_field *field,
const void *data, void *private,
2015-09-21 15:01:41 +03:00
int uuid)
2003-05-06 16:06:02 +04:00
{
const struct logical_volume *lv = (const struct logical_volume *) data;
2015-09-21 15:01:41 +03:00
const char *repstr;
if (uuid)
repstr = lv_move_pv_uuid_dup(mem, lv);
else
repstr = lv_move_pv_dup(mem, lv);
2003-05-06 16:06:02 +04:00
2015-09-21 15:01:41 +03:00
if (repstr)
return _field_string(rh, field, repstr);
return _field_set_value(field, "", NULL);
2003-05-06 16:06:02 +04:00
}
2015-09-21 15:01:41 +03:00
static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_movepv_disp(rh, mem, field, data, private, 0);
}
static int _movepvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_movepv_disp(rh, mem, field, data, private, 1);
}
static int _do_convertlv_disp(struct dm_report *rh, struct dm_pool *mem,
2015-09-21 15:10:21 +03:00
struct dm_report_field *field,
const void *data, void *private,
2015-09-21 15:10:21 +03:00
int uuid)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
const struct logical_volume *convert_lv = lv_convert_lv(lv);
if (!convert_lv)
return _field_set_value(field, "", NULL);
2015-09-21 15:10:21 +03:00
if (uuid)
return _uuid_disp(rh, mem, field, &convert_lv->lvid.id[1], private);
return _lvname_disp(rh, mem, field, convert_lv, private);
}
2015-09-21 15:10:21 +03:00
static int _convertlv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_convertlv_disp(rh, mem, field, data, private, 0);
}
static int _convertlvuuid_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return _do_convertlv_disp(rh, mem, field, data, private, 1);
}
static int _size32_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const uint32_t size = *(const uint32_t *) data;
const char *disp, *repstr;
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
double *sortval;
2008-01-30 16:19:47 +03:00
if (!*(disp = display_size_units(private, (uint64_t) size)))
return_0;
if (!(repstr = dm_pool_strdup(mem, disp))) {
log_error("dm_pool_strdup failed");
return 0;
}
if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
log_error("dm_pool_alloc failed");
return 0;
}
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
*sortval = (double) size;
return _field_set_value(field, repstr, sortval);
}
static int _size64_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const uint64_t size = *(const uint64_t *) data;
const char *disp, *repstr;
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
double *sortval;
2008-01-30 16:19:47 +03:00
if (!*(disp = display_size_units(private, size)))
return_0;
if (!(repstr = dm_pool_strdup(mem, disp))) {
log_error("dm_pool_strdup failed");
return 0;
}
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
if (!(sortval = dm_pool_alloc(mem, sizeof(double)))) {
log_error("dm_pool_alloc failed");
return 0;
}
select: fix matching reserved values while <,<=,>,>= is used in selection criteria Scenario: $ vgs -o+vg_mda_copies VG #PV #LV #SN Attr VSize VFree #VMdaCps fedora 1 2 0 wz--n- 9.51g 0 unmanaged vg 16 9 0 wz--n- 1.94g 1.83g 2 $ lvs -o+read_ahead vg/lvol6 vg/lvol7 LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Before this patch: $vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' VG #VMdaCps vg 2 Problem: Reserved values can be only used with exact match = or !=, not <,<=,>,>=. In the example above, the "unamanaged" is internally represented as 18446744073709551615, but this should be ignored while not comparing field directly with "unmanaged" reserved name with = or !=. Users should not be aware of this internal mapping of the reserved value name to its internal value and hence it doesn't make sense for such reserved value to take place in results of <,<=,> and >=. There's no order defined for reserved values!!! It's a special *reserved* value that is taken out of the usual value range of that type. This is very similar to what we have already fixed with 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but it's the other way round now - we're using reserved value name in selection criteria now (in the patch 2f7f693, we had concrete value and we compared it with the reserved value). So this patch completes patch 2f7f693. This patch also fixes this problem: $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Data% Rahead lvol6 vg Vwi-a-tz-- 1.00g pool lvol5 0.00 auto lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k Problem: In the example above, the internal reserved value "auto" is in the range of selection "> 32k" - it shouldn't match as well. Here the "auto" is internally represented as MAX_DBL and of course, numerically, MAX_DBL > 256k. But for users, the reserved value should be uncomparable to any number so the mapping of the reserved value name to its interna value is transparent to users. Again, there's no order defined for reserved values and hence it should never match if using <,<=,>,>= operators. This is actually exactly the same problem as already described in 2f7f6932dcd450ba75fe590aba8c09838d2618dc, but that patch failed for size field types because of incorrect internal representation used. With this patch applied, both problematic scenarios mentioned above are fixed now: $ vgs -o vg_name,vg_mda_copies -S 'vg_mda_copies < unmanaged' (blank) $ lvs -o+read_ahead vg/lvol6 vg/lvol7 -S 'read_ahead > 32k' LV VG Attr LSize Pool Origin Rahead lvol7 vg Vwi---tz-k 1.00g pool lvol6 256.00k
2015-04-24 10:47:25 +03:00
*sortval = (double) size;
return _field_set_value(field, repstr, sortval);
}
static int _lv_size_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
const struct lv_segment *seg = first_seg(lv);
uint64_t size = lv->le_count;
if (seg && !lv_is_raid_image(lv))
size -= (uint64_t) seg->reshape_len * (seg->area_count > 2 ? (seg->area_count - seg->segtype->parity_devs) : 1);
size *= lv->vg->extent_size;
return _size64_disp(rh, mem, field, &size, private);
}
static int _uint32_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return dm_report_field_uint32(rh, field, data);
}
static int _int8_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const int32_t val = *(const int8_t *)data;
return dm_report_field_int32(rh, field, &val);
}
static int _int32_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
return dm_report_field_int32(rh, field, data);
}
static int _lvwhenfull_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_thin_pool(lv)) {
if (lv->status & LV_ERROR_WHEN_FULL)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_error),
GET_FIELD_RESERVED_VALUE(lv_when_full_error));
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_queue),
GET_FIELD_RESERVED_VALUE(lv_when_full_queue));
}
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_when_full_undef),
GET_FIELD_RESERVED_VALUE(lv_when_full_undef));
}
static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv->read_ahead == DM_READ_AHEAD_AUTO)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(lv_read_ahead_auto),
GET_FIELD_RESERVED_VALUE(lv_read_ahead_auto));
return _size32_disp(rh, mem, field, &lv->read_ahead, private);
}
static int _lvkreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if (!lvdm->info.exists)
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
return _size32_disp(rh, mem, field, &lvdm->info.read_ahead, private);
}
vdo: add lvs fields to query vdo volume properties Add lots of vdo fields: vdo_operating_mode - For vdo pools, its current operating mode. vdo_compression_state - For vdo pools, whether compression is running. vdo_index_state - For vdo pools, state of index for deduplication. vdo_used_size - For vdo pools, currently used space. vdo_saving_percent - For vdo pools, percentage of saved space. vdo_compression - Set for compressed LV (vdopool). vdo_deduplication - Set for deduplicated LV (vdopool). vdo_use_metadata_hints - Use REQ_SYNC for writes (vdopool). vdo_minimum_io_size - Minimum acceptable IO size (vdopool). vdo_block_map_cache_size - Allocated caching size (vdopool). vdo_block_map_era_length - Speed of cache writes (vdopool). vdo_use_sparse_index - Sparse indexing (vdopool). vdo_index_memory_size - Allocated indexing memory (vdopool). vdo_slab_size - Increment size for growing (vdopool). vdo_ack_threads - Acknowledging threads (vdopool). vdo_bio_threads - IO submitting threads (vdopool). vdo_bio_rotation - IO enqueue (vdopool). vdo_cpu_threads - CPU threads for compression and hashing (vdopool). vdo_hash_zone_threads - Threads for subdivide parts (vdopool). vdo_logical_threads - Logical threads for subdivide parts (vdopool). vdo_physical_threads - Physical threads for subdivide parts (vdopool). vdo_max_discard - Maximum discard size volume can recieve (vdopool). vdo_write_policy - Specified write policy (vdopool). vdo_header_size - Header size at front of vdopool. Previously only 'lvdisplay -m' was exposing them.
2019-10-04 18:02:20 +03:00
static int _vdo_operating_mode_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
return _field_string(rh, field, get_vdo_operating_mode_name(lvdm->seg_status.vdo_pool.vdo->operating_mode));
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_compression_state_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
return _field_string(rh, field, get_vdo_compression_state_name(lvdm->seg_status.vdo_pool.vdo->compression_state));
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_index_state_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
return _field_string(rh, field, get_vdo_index_state_name(lvdm->seg_status.vdo_pool.vdo->index_state));
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_used_size_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
uint64_t size;
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL)) {
size = lvdm->seg_status.vdo_pool.vdo->used_blocks * DM_VDO_BLOCK_SIZE;
return _size64_disp(rh, mem, field, &size, private);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_saving_percent_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if ((lv_is_vdo_pool(lvdm->lv) || lv_is_vdo(lvdm->lv)) &&
(lvdm->seg_status.type == SEG_STATUS_VDO_POOL))
return dm_report_field_percent(rh, field, &lvdm->seg_status.vdo_pool.saving);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vgsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint64_t size = vg_size(vg);
return _size64_disp(rh, mem, field, &size, private);
}
static int _segmonitor_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *)data;
char *str;
if (!(str = lvseg_monitor_dup(mem, seg)))
return_0;
report: fix seg_monitor field to display monitoring status for thick snapshots and mirrors The seg_monitor did not display monitored status for thick snapshots and mirrors (with mirror log *not* mirrored). The seg monitor did work correctly even before for other segtypes - thins and raids. Before (mirrors and snapshots, only mirrors with mirrored log properly displayed monitoring status): [0] f21/~ # lvs -a -o lv_name,lv_layout,lv_role,seg_monitor vg LV Layout Role Monitor mirror mirror public [mirror_mimage_0] linear private,mirror,image [mirror_mimage_1] linear private,mirror,image [mirror_mlog] linear private,mirror,log mirror_with_mirror_log mirror public monitored [mirror_with_mirror_log_mimage_0] linear private,mirror,image [mirror_with_mirror_log_mimage_1] linear private,mirror,image [mirror_with_mirror_log_mlog] mirror private,mirror,log monitored [mirror_with_mirror_log_mlog_mimage_0] linear private,mirror,image [mirror_with_mirror_log_mlog_mimage_1] linear private,mirror,image thick_origin linear public,origin,thickorigin thick_snapshot linear public,snapshot,thicksnapshot With this patch applied (monitoring status displayed for all mirrors and snapshots): [0] f21/~ # lvs -a -o lv_name,lv_layout,lv_role,seg_monitor vg LV Layout Role Monitor mirror mirror public monitored [mirror_mimage_0] linear private,mirror,image [mirror_mimage_1] linear private,mirror,image [mirror_mlog] linear private,mirror,log mirror_with_mirror_log mirror public monitored [mirror_with_mirror_log_mimage_0] linear private,mirror,image [mirror_with_mirror_log_mimage_1] linear private,mirror,image [mirror_with_mirror_log_mlog] mirror private,mirror,log monitored [mirror_with_mirror_log_mlog_mimage_0] linear private,mirror,image [mirror_with_mirror_log_mlog_mimage_1] linear private,mirror,image thick_origin linear public,origin,thickorigin thick_snapshot linear public,snapshot,thicksnapshot monitored
2015-03-05 12:45:29 +03:00
if (*str)
return _field_set_value(field, str, NULL);
return _field_set_value(field, GET_FIRST_RESERVED_NAME(seg_monitor_undef),
GET_FIELD_RESERVED_VALUE(seg_monitor_undef));
}
static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint64_t start = lvseg_start(seg);
return _size64_disp(rh, mem, field, &start, private);
}
2007-12-20 19:49:37 +03:00
static int _segstartpe_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
2007-12-20 19:49:37 +03:00
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct lv_segment *seg = (const struct lv_segment *) data;
return dm_report_field_uint32(rh, field, &seg->le);
}
/* Hepler: get used stripes = total stripes minux any to remove after reshape */
static int _get_seg_used_stripes(const struct lv_segment *seg)
{
uint32_t s;
uint32_t stripes = seg->area_count;
for (s = seg->area_count - 1; stripes && s; s--) {
if (seg_type(seg, s) == AREA_LV &&
seg_lv(seg, s)->status & LV_REMOVE_AFTER_RESHAPE)
stripes--;
else
break;
}
return stripes;
}
static int _seg_stripes_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = ((const struct lv_segment *) data);
return dm_report_field_uint32(rh, field, &seg->area_count);
}
/* Report the number of data stripes, which is less than total stripes (e.g. 2 less for raid6) */
static int _seg_data_stripes_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint32_t stripes = _get_seg_used_stripes(seg) - seg->segtype->parity_devs;
/* FIXME: in case of odd numbers of raid10 stripes */
if (seg_is_raid10(seg))
stripes /= seg->data_copies;
return dm_report_field_uint32(rh, field, &stripes);
}
/* Helper: return the top-level, reshapable raid LV in case @seg belongs to an raid rimage LV */
static struct logical_volume *_lv_for_raid_image_seg(const struct lv_segment *seg, struct dm_pool *mem)
{
char *lv_name;
if (seg_is_reshapable_raid(seg))
return seg->lv;
if (seg->lv &&
lv_is_raid_image(seg->lv) && !seg->le &&
(lv_name = dm_pool_strdup(mem, seg->lv->name))) {
char *p = strchr(lv_name, '_');
if (p) {
/* Handle duplicated sub LVs */
if (strstr(p, "_dup_"))
p = strchr(p + 5, '_');
if (p) {
struct lv_list *lvl;
*p = '\0';
if ((lvl = find_lv_in_vg(seg->lv->vg, lv_name)) &&
seg_is_reshapable_raid(first_seg(lvl->lv)))
return lvl->lv;
}
}
}
return NULL;
}
/* Helper: return the top-level raid LV in case it is reshapale for @seg or @seg if it is */
static const struct lv_segment *_get_reshapable_seg(const struct lv_segment *seg, struct dm_pool *mem)
{
return _lv_for_raid_image_seg(seg, mem) ? seg : NULL;
}
/* Display segment reshape length in current units */
static int _seg_reshape_len_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = _get_reshapable_seg((const struct lv_segment *) data, mem);
if (seg) {
uint32_t reshape_len = seg->reshape_len * seg->area_count * seg->lv->vg->extent_size;
return _size32_disp(rh, mem, field, &reshape_len, private);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
/* Display segment reshape length of in logical extents */
static int _seg_reshape_len_le_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = _get_reshapable_seg((const struct lv_segment *) data, mem);
if (seg) {
uint32_t reshape_len = seg->reshape_len* seg->area_count;
return dm_report_field_uint32(rh, field, &reshape_len);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
/* Display segment data copies (e.g. 3 for raid6) */
static int _seg_data_copies_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg->data_copies)
return dm_report_field_uint32(rh, field, &seg->data_copies);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
/* Helper: display segment data offset/new data offset in sectors */
static int _segdata_offset(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private, int new_data_offset)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
struct logical_volume *lv;
if ((lv = _lv_for_raid_image_seg(seg, mem))) {
uint64_t data_offset = 0;
if (lv_raid_data_offset(lv, &data_offset)) {
if (new_data_offset && lv_is_raid_image(lv) && !lv_raid_image_in_sync(lv))
data_offset = data_offset ? 0 : (uint64_t) seg->reshape_len * lv->vg->extent_size;
return dm_report_field_uint64(rh, field, &data_offset);
}
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _seg_data_offset_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _segdata_offset(rh, mem, field, data, private, 0);
}
static int _seg_new_data_offset_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
return _segdata_offset(rh, mem, field, data, private, 1);
}
static int _seg_parity_chunks_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint32_t parity_chunks = seg->segtype->parity_devs ?: seg->data_copies - 1;
if (parity_chunks) {
uint32_t s, resilient_sub_lvs = 0;
for (s = 0; s < seg->area_count; s++) {
if (seg_type(seg, s) == AREA_LV) {
struct lv_segment *seg1 = first_seg(seg_lv(seg, s));
if (seg1->segtype->parity_devs ||
seg1->data_copies > 1)
resilient_sub_lvs++;
}
}
if (resilient_sub_lvs && resilient_sub_lvs == seg->area_count)
parity_chunks++;
return dm_report_field_uint32(rh, field, &parity_chunks);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint64_t size = lvseg_size(seg);
return _size64_disp(rh, mem, field, &size, private);
}
static int _segsizepe_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct lv_segment *seg = (const struct lv_segment *) data;
return dm_report_field_uint32(rh, field, &seg->len);
}
static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
2005-09-23 21:06:01 +04:00
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint64_t size = lvseg_chunksize(seg);
return _size64_disp(rh, mem, field, &size, private);
}
static int _transactionid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_thin_pool(seg) || seg_is_thin_volume(seg))
return dm_report_field_uint64(rh, field, &seg->transaction_id);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _thinid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_thin_volume(seg))
return dm_report_field_uint32(rh, field, &seg->device_id);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _discards_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
const char *discards_str;
if (seg_is_thin_volume(seg))
seg = first_seg(seg->pool_lv);
if (seg_is_thin_pool(seg)) {
discards_str = get_pool_discards_name(seg->discards);
return _field_string(rh, field, discards_str);
}
return _field_set_value(field, "", NULL);
}
static int _kdiscards_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
const char *discards_str;
if (!(discards_str = lvseg_kernel_discards_dup_with_info_and_seg_status(mem, lvdm)))
return_0;
if (*discards_str)
return _field_set_value(field, discards_str, NULL);
return _field_set_value(field, GET_FIRST_RESERVED_NAME(seg_kernel_discards_undef),
GET_FIELD_RESERVED_VALUE(seg_kernel_discards_undef));
}
static int _cachemode_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
return _field_string(rh, field, display_cache_mode(seg));
}
static int _cachemetadataformat_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
const struct lv_segment *setting_seg = NULL;
const uint64_t *fmt;
if (seg_is_cache(seg) && lv_is_cache_vol(seg->pool_lv))
Allow dm-cache cache device to be standard LV If a single, standard LV is specified as the cache, use it directly instead of converting it into a cache-pool object with two separate LVs (for data and metadata). With a single LV as the cache, lvm will use blocks at the beginning for metadata, and the rest for data. Separate dm linear devices are set up to point at the metadata and data areas of the LV. These dm devs are given to the dm-cache target to use. The single LV cache cannot be resized without recreating it. If the --poolmetadata option is used to specify an LV for metadata, then a cache pool will be created (with separate LVs for data and metadata.) Usage: $ lvcreate -n main -L 128M vg /dev/loop0 $ lvcreate -n fast -L 64M vg /dev/loop1 $ lvs -a vg LV VG Attr LSize Type Devices main vg -wi-a----- 128.00m linear /dev/loop0(0) fast vg -wi-a----- 64.00m linear /dev/loop1(0) $ lvconvert --type cache --cachepool fast vg/main $ lvs -a vg LV VG Attr LSize Origin Pool Type Devices [fast] vg Cwi---C--- 64.00m linear /dev/loop1(0) main vg Cwi---C--- 128.00m [main_corig] [fast] cache main_corig(0) [main_corig] vg owi---C--- 128.00m linear /dev/loop0(0) $ lvchange -ay vg/main $ dmsetup ls vg-fast_cdata (253:4) vg-fast_cmeta (253:5) vg-main_corig (253:6) vg-main (253:24) vg-fast (253:3) $ dmsetup table vg-fast_cdata: 0 98304 linear 253:3 32768 vg-fast_cmeta: 0 32768 linear 253:3 0 vg-main_corig: 0 262144 linear 7:0 2048 vg-main: 0 262144 cache 253:5 253:4 253:6 128 2 metadata2 writethrough mq 0 vg-fast: 0 131072 linear 7:1 2048 $ lvchange -an vg/min $ lvconvert --splitcache vg/main $ lvs -a vg LV VG Attr LSize Type Devices fast vg -wi------- 64.00m linear /dev/loop1(0) main vg -wi------- 128.00m linear /dev/loop0(0)
2018-08-17 23:45:52 +03:00
setting_seg = seg;
else if (seg_is_cache_pool(seg))
setting_seg = seg;
else if (seg_is_cache(seg))
setting_seg = first_seg(seg->pool_lv);
else
goto undef;
switch (setting_seg->cache_metadata_format) {
case CACHE_METADATA_FORMAT_1:
case CACHE_METADATA_FORMAT_2:
fmt = (setting_seg->cache_metadata_format == CACHE_METADATA_FORMAT_2) ? &_two64 : &_one64;
return dm_report_field_uint64(rh, field, fmt);
default: /* unselected/undefined for all other cases */;
}
undef:
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
uint64_t size = lv_origin_size(lv);
if (size)
return _size64_disp(rh, mem, field, &size, private);
2005-09-23 21:06:01 +04:00
return _field_set_value(field, "", &_zero64);
2005-09-23 21:06:01 +04:00
}
static int _pvused_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
uint64_t used = pv_used(pv);
return _size64_disp(rh, mem, field, &used, private);
}
static int _pvfree_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
uint64_t freespace;
if (is_orphan(pv) && is_used_pv(pv))
freespace = 0;
else
freespace = pv_free(pv);
return _size64_disp(rh, mem, field, &freespace, private);
}
static int _pvsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
uint64_t size = pv_size_field(pv);
return _size64_disp(rh, mem, field, &size, private);
}
static int _devsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
2004-08-11 17:15:05 +04:00
{
struct device *dev = *(struct device * const *) data;
uint64_t size;
if (!dev || !dev->dev || !dev_get_size(dev, &size))
size = _zero64;
return _size64_disp(rh, mem, field, &size, private);
2004-08-11 17:15:05 +04:00
}
static int _vgfree_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint64_t freespace = vg_free(vg);
return _size64_disp(rh, mem, field, &freespace, private);
}
static int _vgsystemid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
const char *repstr = (vg->system_id && *vg->system_id) ? vg->system_id : "";
return _field_string(rh, field, repstr);
}
2015-03-05 23:00:44 +03:00
static int _vglocktype_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
const char *locktype;
2015-03-05 23:00:44 +03:00
if (!vg->lock_type || !strcmp(vg->lock_type, "none"))
locktype = "";
else
locktype = vg->lock_type;
return _field_string(rh, field, locktype);
2015-03-05 23:00:44 +03:00
}
static int _vglockargs_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
return _field_string(rh, field, vg->lock_args ? : "");
2015-03-05 23:00:44 +03:00
}
static int _lvuuid_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
const union lvid *lvid;
char *repstr;
if (lv_is_historical(lv))
lvid = &lv->this_glv->historical->lvid;
else
lvid = &lv->lvid;
if (!(repstr = id_format_and_copy(mem, &lvid->id[1])))
return_0;
return _field_set_value(field, repstr, NULL);
}
static int _pvuuid_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct label *label = (const struct label *) data;
if (!label->dev)
return _field_set_value(field, "", NULL);
return _uuid_disp(rh, mem, field, label->dev->pvid, private);
}
static int _pvmdas_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
uint32_t count = pv_mda_count(pv);
return _uint32_disp(rh, mem, field, &count, private);
}
Define new functions and vgs/pvs fields related to mda ignore. Define a new pvs field, pv_mda_used_count, and a new vgs field, vg_mda_used_count to match the existing pv_mda_count and vg_mda_count. These new fields count the number of mdas that have the 'ignored' bit clear (they are in use on the PV / VG). Also define various supporting functions to implement the counting as well as setting the ignored flag and determining if an mda is ignored. These high level functions call into the lower level location independent mda ignore functions defined by earlier patches. Note that counting ignored mdas in a vg requires traversing both lists and checking for the ignored bit on the mda. The count of 'ignored' mdas then is defined by having the bit set, not by which list the mda is on. The list does determine whether LVM actually does read/write to the mda, though we must count the bits in order to return accurate numbers for the various counts. Also, pv_mda_set_ignored must search both vg lists for ignored mda. If the state changes and needs to be committed to disk, the ignored mda will be on the non-ignored list. Note also in pv_mda_set_ignored(), we must properly manage the mda lists. If we change the ignored state of an mda, we must change any mdas on vg->fid->metadata_areas that correspond to this pv. Also, we may need to allocate a copy of the mda, as is done when fid->metadata_areas is populated from _vg_read(), if we are un-ignoring an ignored mda. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:33:44 +04:00
static int _pvmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv =
(const struct physical_volume *) data;
uint32_t count = pv_mda_used_count(pv);
Define new functions and vgs/pvs fields related to mda ignore. Define a new pvs field, pv_mda_used_count, and a new vgs field, vg_mda_used_count to match the existing pv_mda_count and vg_mda_count. These new fields count the number of mdas that have the 'ignored' bit clear (they are in use on the PV / VG). Also define various supporting functions to implement the counting as well as setting the ignored flag and determining if an mda is ignored. These high level functions call into the lower level location independent mda ignore functions defined by earlier patches. Note that counting ignored mdas in a vg requires traversing both lists and checking for the ignored bit on the mda. The count of 'ignored' mdas then is defined by having the bit set, not by which list the mda is on. The list does determine whether LVM actually does read/write to the mda, though we must count the bits in order to return accurate numbers for the various counts. Also, pv_mda_set_ignored must search both vg lists for ignored mda. If the state changes and needs to be committed to disk, the ignored mda will be on the non-ignored list. Note also in pv_mda_set_ignored(), we must properly manage the mda lists. If we change the ignored state of an mda, we must change any mdas on vg->fid->metadata_areas that correspond to this pv. Also, we may need to allocate a copy of the mda, as is done when fid->metadata_areas is populated from _vg_read(), if we are un-ignoring an ignored mda. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:33:44 +04:00
return _uint32_disp(rh, mem, field, &count, private);
}
static int _vgmdas_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = vg_mda_count(vg);
return _uint32_disp(rh, mem, field, &count, private);
}
Define new functions and vgs/pvs fields related to mda ignore. Define a new pvs field, pv_mda_used_count, and a new vgs field, vg_mda_used_count to match the existing pv_mda_count and vg_mda_count. These new fields count the number of mdas that have the 'ignored' bit clear (they are in use on the PV / VG). Also define various supporting functions to implement the counting as well as setting the ignored flag and determining if an mda is ignored. These high level functions call into the lower level location independent mda ignore functions defined by earlier patches. Note that counting ignored mdas in a vg requires traversing both lists and checking for the ignored bit on the mda. The count of 'ignored' mdas then is defined by having the bit set, not by which list the mda is on. The list does determine whether LVM actually does read/write to the mda, though we must count the bits in order to return accurate numbers for the various counts. Also, pv_mda_set_ignored must search both vg lists for ignored mda. If the state changes and needs to be committed to disk, the ignored mda will be on the non-ignored list. Note also in pv_mda_set_ignored(), we must properly manage the mda lists. If we change the ignored state of an mda, we must change any mdas on vg->fid->metadata_areas that correspond to this pv. Also, we may need to allocate a copy of the mda, as is done when fid->metadata_areas is populated from _vg_read(), if we are un-ignoring an ignored mda. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:33:44 +04:00
static int _vgmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = vg_mda_used_count(vg);
Define new functions and vgs/pvs fields related to mda ignore. Define a new pvs field, pv_mda_used_count, and a new vgs field, vg_mda_used_count to match the existing pv_mda_count and vg_mda_count. These new fields count the number of mdas that have the 'ignored' bit clear (they are in use on the PV / VG). Also define various supporting functions to implement the counting as well as setting the ignored flag and determining if an mda is ignored. These high level functions call into the lower level location independent mda ignore functions defined by earlier patches. Note that counting ignored mdas in a vg requires traversing both lists and checking for the ignored bit on the mda. The count of 'ignored' mdas then is defined by having the bit set, not by which list the mda is on. The list does determine whether LVM actually does read/write to the mda, though we must count the bits in order to return accurate numbers for the various counts. Also, pv_mda_set_ignored must search both vg lists for ignored mda. If the state changes and needs to be committed to disk, the ignored mda will be on the non-ignored list. Note also in pv_mda_set_ignored(), we must properly manage the mda lists. If we change the ignored state of an mda, we must change any mdas on vg->fid->metadata_areas that correspond to this pv. Also, we may need to allocate a copy of the mda, as is done when fid->metadata_areas is populated from _vg_read(), if we are un-ignoring an ignored mda. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:33:44 +04:00
return _uint32_disp(rh, mem, field, &count, private);
}
static int _vgmdacopies_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
struct cmd_context *cmd = (struct cmd_context *) private;
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = vg_mda_copies(vg);
if (count == VGMETADATACOPIES_UNMANAGED && !cmd->report_strict_type_mode)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(vg_mda_copies_unmanaged),
GET_FIELD_RESERVED_VALUE(vg_mda_copies_unmanaged));
return _uint32_disp(rh, mem, field, &count, private);
}
static int _vgprofile_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
if (vg->profile)
return _field_string(rh, field, vg->profile->name);
return _field_set_value(field, "", NULL);
}
static int _vgmissingpvcount_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = vg_missing_pv_count(vg);
return _uint32_disp(rh, mem, field, &count, private);
}
static int _pvmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct label *label = (const struct label *) data;
uint64_t freespace = lvmcache_info_mda_free(label->info);
return _size64_disp(rh, mem, field, &freespace, private);
}
static int _pvmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct label *label = (const struct label *) data;
uint64_t min_mda_size = lvmcache_smallest_mda_size(label->info);
return _size64_disp(rh, mem, field, &min_mda_size, private);
}
static int _pvextvsn_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct label *label = (const struct label *) data;
struct lvmcache_info *info = label->info;
uint32_t ext_version;
if (info) {
ext_version = lvmcache_ext_version(info);
return _uint32_disp(rh, mem, field, &ext_version, private);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vgmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint64_t min_mda_size = vg_mda_size(vg);
return _size64_disp(rh, mem, field, &min_mda_size, private);
}
static int _vgmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint64_t freespace = vg_mda_free(vg);
return _size64_disp(rh, mem, field, &freespace, private);
}
static int _lvcount_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = vg_visible_lvs(vg);
return _uint32_disp(rh, mem, field, &count, private);
}
static int _lvsegcount_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
uint32_t count = dm_list_size(&lv->segments);
return _uint32_disp(rh, mem, field, &count, private);
}
static int _snapcount_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *) data;
uint32_t count = snapshot_count(vg);
return _uint32_disp(rh, mem, field, &count, private);
}
static int _snpercent_disp(struct dm_report *rh, struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
dm_percent_t percent = lvseg_percent_with_info_and_seg_status(lvdm, PERCENT_GET_DATA);
2003-01-21 21:50:50 +03:00
return dm_report_field_percent(rh, field, &percent);
}
static int _copypercent_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute__((unused)))
2003-05-06 16:06:02 +04:00
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
const struct logical_volume *lv = lvdm->lv;
dm_percent_t percent = DM_PERCENT_INVALID;
2003-05-06 16:06:02 +04:00
/* TODO: just cache passes through lvseg_percent... */
Allow dm-integrity to be used for raid images dm-integrity stores checksums of the data written to an LV, and returns an error if data read from the LV does not match the previously saved checksum. When used on raid images, dm-raid will correct the error by reading the block from another image, and the device user sees no error. The integrity metadata (checksums) are stored on an internal LV allocated by lvm for each linear image. The internal LV is allocated on the same PV as the image. Create a raid LV with an integrity layer over each raid image (for raid levels 1,4,5,6,10): lvcreate --type raidN --raidintegrity y [options] Add an integrity layer to images of an existing raid LV: lvconvert --raidintegrity y LV Remove the integrity layer from images of a raid LV: lvconvert --raidintegrity n LV Settings Use --raidintegritymode journal|bitmap (journal is default) to configure the method used by dm-integrity to ensure crash consistency. Initialization When integrity is added to an LV, the kernel needs to initialize the integrity metadata/checksums for all blocks in the LV. The data corruption checking performed by dm-integrity will only operate on areas of the LV that are already initialized. The progress of integrity initialization is reported by the "syncpercent" LV reporting field (and under the Cpy%Sync lvs column.) Example: create a raid1 LV with integrity: $ lvcreate --type raid1 -m1 --raidintegrity y -n rr -L1G foo Creating integrity metadata LV rr_rimage_0_imeta with size 12.00 MiB. Logical volume "rr_rimage_0_imeta" created. Creating integrity metadata LV rr_rimage_1_imeta with size 12.00 MiB. Logical volume "rr_rimage_1_imeta" created. Logical volume "rr" created. $ lvs -a foo LV VG Attr LSize Origin Cpy%Sync rr foo rwi-a-r--- 1.00g 4.93 [rr_rimage_0] foo gwi-aor--- 1.00g [rr_rimage_0_iorig] 41.02 [rr_rimage_0_imeta] foo ewi-ao---- 12.00m [rr_rimage_0_iorig] foo -wi-ao---- 1.00g [rr_rimage_1] foo gwi-aor--- 1.00g [rr_rimage_1_iorig] 39.45 [rr_rimage_1_imeta] foo ewi-ao---- 12.00m [rr_rimage_1_iorig] foo -wi-ao---- 1.00g [rr_rmeta_0] foo ewi-aor--- 4.00m [rr_rmeta_1] foo ewi-aor--- 4.00m
2019-11-21 01:07:27 +03:00
if (lv_is_integrity(lv) || lv_is_cache(lv) || lv_is_used_cache_pool(lv) ||
(!lv_is_merging_origin(lv) && lv_is_raid(lv) && !seg_is_any_raid0(first_seg(lv))))
percent = lvseg_percent_with_info_and_seg_status(lvdm, PERCENT_GET_DIRTY);
else if (lv_is_raid(lv) && !seg_is_any_raid0(first_seg(lv)))
/* old way for percentage when merging snapshot into raid origin */
(void) lv_raid_percent(lv, &percent);
else if (((lv_is_mirror(lv) &&
lv_mirror_percent(lv->vg->cmd, lv, 0, &percent, NULL))) &&
(percent != DM_PERCENT_INVALID))
percent = copy_percent(lv);
2003-05-06 16:06:02 +04:00
return dm_report_field_percent(rh, field, &percent);
2003-05-06 16:06:02 +04:00
}
static int _raidsyncaction_disp(struct dm_report *rh __attribute__((unused)),
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *sync_action;
if (lv_is_raid(lv) && lv_raid_sync_action(lv, &sync_action))
return _field_string(rh, field, sync_action);
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
return _field_set_value(field, "", NULL);
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
}
static int _raidmismatchcount_disp(struct dm_report *rh __attribute__((unused)),
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
uint64_t mismatch_count;
if (lv_is_raid(lv) && lv_raid_mismatch_count(lv, &mismatch_count))
return dm_report_field_uint64(rh, field, &mismatch_count);
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
RAID: Add writemostly/writebehind support for RAID1 'lvchange' is used to alter a RAID 1 logical volume's write-mostly and write-behind characteristics. The '--writemostly' parameter takes a PV as an argument with an optional trailing character to specify whether to set ('y'), unset ('n'), or toggle ('t') the value. If no trailing character is given, it will set the flag. Synopsis: lvchange [--writemostly <PV>:{t|y|n}] [--writebehind <count>] vg/lv Example: lvchange --writemostly /dev/sdb1:y --writebehind 512 vg/raid1_lv The last character in the 'lv_attr' field is used to show whether a device has the WriteMostly flag set. It is signified with a 'w'. If the device has failed, the 'p'artial flag has priority. Example ("nosync" raid1 with mismatch_cnt and writemostly): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg Rwi---r-m 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-w 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-- 1 linear 4.00m Example (raid1 with mismatch_cnt, writemostly - but failed drive): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg rwi---r-p 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-p 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-p 1 linear 4.00m A new reportable field has been added for writebehind as well. If write-behind has not been set or the LV is not RAID1, the field will be blank. Example (writebehind is set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- 512 [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor-- Example (writebehind is not set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor--
2013-04-15 22:59:46 +04:00
}
static int _raidwritebehind_disp(struct dm_report *rh __attribute__((unused)),
RAID: Add writemostly/writebehind support for RAID1 'lvchange' is used to alter a RAID 1 logical volume's write-mostly and write-behind characteristics. The '--writemostly' parameter takes a PV as an argument with an optional trailing character to specify whether to set ('y'), unset ('n'), or toggle ('t') the value. If no trailing character is given, it will set the flag. Synopsis: lvchange [--writemostly <PV>:{t|y|n}] [--writebehind <count>] vg/lv Example: lvchange --writemostly /dev/sdb1:y --writebehind 512 vg/raid1_lv The last character in the 'lv_attr' field is used to show whether a device has the WriteMostly flag set. It is signified with a 'w'. If the device has failed, the 'p'artial flag has priority. Example ("nosync" raid1 with mismatch_cnt and writemostly): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg Rwi---r-m 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-w 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-- 1 linear 4.00m Example (raid1 with mismatch_cnt, writemostly - but failed drive): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg rwi---r-p 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-p 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-p 1 linear 4.00m A new reportable field has been added for writebehind as well. If write-behind has not been set or the LV is not RAID1, the field will be blank. Example (writebehind is set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- 512 [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor-- Example (writebehind is not set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor--
2013-04-15 22:59:46 +04:00
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_raid_type(lv) && first_seg(lv)->writebehind)
return dm_report_field_uint32(rh, field, &first_seg(lv)->writebehind);
RAID: Add writemostly/writebehind support for RAID1 'lvchange' is used to alter a RAID 1 logical volume's write-mostly and write-behind characteristics. The '--writemostly' parameter takes a PV as an argument with an optional trailing character to specify whether to set ('y'), unset ('n'), or toggle ('t') the value. If no trailing character is given, it will set the flag. Synopsis: lvchange [--writemostly <PV>:{t|y|n}] [--writebehind <count>] vg/lv Example: lvchange --writemostly /dev/sdb1:y --writebehind 512 vg/raid1_lv The last character in the 'lv_attr' field is used to show whether a device has the WriteMostly flag set. It is signified with a 'w'. If the device has failed, the 'p'artial flag has priority. Example ("nosync" raid1 with mismatch_cnt and writemostly): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg Rwi---r-m 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-w 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-- 1 linear 4.00m Example (raid1 with mismatch_cnt, writemostly - but failed drive): [~]# lvs -a --segment vg LV VG Attr #Str Type SSize raid1 vg rwi---r-p 2 raid1 500.00m [raid1_rimage_0] vg Iwi---r-- 1 linear 500.00m [raid1_rimage_1] vg Iwi---r-p 1 linear 500.00m [raid1_rmeta_0] vg ewi---r-- 1 linear 4.00m [raid1_rmeta_1] vg ewi---r-p 1 linear 4.00m A new reportable field has been added for writebehind as well. If write-behind has not been set or the LV is not RAID1, the field will be blank. Example (writebehind is set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- 512 [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor-- Example (writebehind is not set): [~]# lvs -a -o name,attr,writebehind vg LV Attr WBehind lv rwi-a-r-- [lv_rimage_0] iwi-aor-w [lv_rimage_1] iwi-aor-- [lv_rmeta_0] ewi-aor-- [lv_rmeta_1] ewi-aor--
2013-04-15 22:59:46 +04:00
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
RAID: Add scrubbing support for RAID LVs New options to 'lvchange' allow users to scrub their RAID LVs. Synopsis: lvchange --syncaction {check|repair} vg/raid_lv RAID scrubbing is the process of reading all the data and parity blocks in an array and checking to see whether they are coherent. 'lvchange' can now initaite the two scrubbing operations: "check" and "repair". "check" will go over the array and recored the number of discrepancies but not repair them. "repair" will correct the discrepancies as it finds them. 'lvchange --syncaction repair vg/raid_lv' is not to be confused with 'lvconvert --repair vg/raid_lv'. The former initiates a background synchronization operation on the array, while the latter is designed to repair/replace failed devices in a mirror or RAID logical volume. Additional reporting has been added for 'lvs' to support the new operations. Two new printable fields (which are not printed by default) have been added: "syncaction" and "mismatches". These can be accessed using the '-o' option to 'lvs', like: lvs -o +syncaction,mismatches vg/lv "syncaction" will print the current synchronization operation that the RAID volume is performing. It can be one of the following: - idle: All sync operations complete (doing nothing) - resync: Initializing an array or recovering after a machine failure - recover: Replacing a device in the array - check: Looking for array inconsistencies - repair: Looking for and repairing inconsistencies The "mismatches" field with print the number of descrepancies found during a check or repair operation. The 'Cpy%Sync' field already available to 'lvs' will print the progress of any of the above syncactions, including check and repair. Finally, the lv_attr field has changed to accomadate the scrubbing operations as well. The role of the 'p'artial character in the lv_attr report field as expanded. "Partial" is really an indicator for the health of a logical volume and it makes sense to extend this include other health indicators as well, specifically: 'm'ismatches: Indicates that there are discrepancies in a RAID LV. This character is shown after a scrubbing operation has detected that portions of the RAID are not coherent. 'r'efresh : Indicates that a device in a RAID array has suffered a failure and the kernel regards it as failed - even though LVM can read the device label and considers the device to be ok. The LV should be 'r'efreshed to notify the kernel that the device is now available, or the device should be 'r'eplaced if it is suspected of failing.
2013-04-12 00:33:59 +04:00
}
static int _raidminrecoveryrate_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_raid_type(lv) && first_seg(lv)->min_recovery_rate)
return dm_report_field_uint32(rh, field,
&first_seg(lv)->min_recovery_rate);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _raidmaxrecoveryrate_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_raid_type(lv) && first_seg(lv)->max_recovery_rate)
return dm_report_field_uint32(rh, field,
&first_seg(lv)->max_recovery_rate);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _raidintegritymode_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
struct logical_volume *lv = (struct logical_volume *) data;
2021-03-09 18:32:36 +03:00
struct integrity_settings *settings = NULL;
const char *mode = NULL;
char *repstr;
if (lv_raid_has_integrity(lv))
lv_get_raid_integrity_settings(lv, &settings);
else if (lv_is_integrity(lv))
settings = &first_seg(lv)->integrity_settings;
else
goto out;
2021-03-09 18:32:36 +03:00
if (settings && settings->mode[0]) {
if (settings->mode[0] == 'B')
mode = "bitmap";
else if (settings->mode[0] == 'J')
mode = "journal";
if (mode) {
if (!(repstr = dm_pool_strdup(mem, mode))) {
log_error("Failed to allocate buffer for mode.");
return 0;
}
return _field_set_value(field, repstr, NULL);
}
}
out:
return _field_set_value(field, "", NULL);
}
static int _raidintegrityblocksize_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
struct logical_volume *lv = (struct logical_volume *) data;
2021-03-09 18:32:36 +03:00
struct integrity_settings *settings = NULL;
if (lv_raid_has_integrity(lv))
lv_get_raid_integrity_settings(lv, &settings);
else if (lv_is_integrity(lv))
settings = &first_seg(lv)->integrity_settings;
2021-03-09 18:32:36 +03:00
if (!settings)
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
return dm_report_field_uint32(rh, field, &settings->block_size);
}
static int _integritymismatches_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
struct logical_volume *lv = (struct logical_volume *) data;
uint64_t mismatches = 0;
if (lv_is_integrity(lv) && lv_integrity_mismatches(lv->vg->cmd, lv, &mismatches))
return dm_report_field_uint64(rh, field, &mismatches);
if (lv_is_raid(lv) && lv_raid_has_integrity(lv) &&
lv_raid_integrity_total_mismatches(lv->vg->cmd, lv, &mismatches))
return dm_report_field_uint64(rh, field, &mismatches);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _writecache_block_size_disp(struct dm_report *rh __attribute__((unused)),
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private __attribute__((unused)))
{
struct logical_volume *lv = (struct logical_volume *) data;
uint32_t bs = 0;
if (lv_is_writecache(lv)) {
struct lv_segment *seg = first_seg(lv);
bs = seg->writecache_block_size;
}
if (!bs)
return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
return dm_report_field_uint32(rh, field, &bs);
}
static int _datapercent_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
dm_percent_t percent = lvseg_percent_with_info_and_seg_status(lvdm, PERCENT_GET_DATA);
return dm_report_field_percent(rh, field, &percent);
}
static int _metadatapercent_disp(struct dm_report *rh,
struct dm_pool *mem __attribute__((unused)),
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
dm_percent_t percent;
switch (lvdm->seg_status.type) {
case SEG_STATUS_CACHE:
case SEG_STATUS_THIN_POOL:
percent = lvseg_percent_with_info_and_seg_status(lvdm, PERCENT_GET_METADATA);
break;
default:
percent = DM_PERCENT_INVALID;
}
return dm_report_field_percent(rh, field, &percent);
}
static int _lvmetadatasize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
uint64_t size;
if (lv_is_cache(lv) && lv_is_cache_vol(first_seg(lv)->pool_lv)) {
Allow dm-cache cache device to be standard LV If a single, standard LV is specified as the cache, use it directly instead of converting it into a cache-pool object with two separate LVs (for data and metadata). With a single LV as the cache, lvm will use blocks at the beginning for metadata, and the rest for data. Separate dm linear devices are set up to point at the metadata and data areas of the LV. These dm devs are given to the dm-cache target to use. The single LV cache cannot be resized without recreating it. If the --poolmetadata option is used to specify an LV for metadata, then a cache pool will be created (with separate LVs for data and metadata.) Usage: $ lvcreate -n main -L 128M vg /dev/loop0 $ lvcreate -n fast -L 64M vg /dev/loop1 $ lvs -a vg LV VG Attr LSize Type Devices main vg -wi-a----- 128.00m linear /dev/loop0(0) fast vg -wi-a----- 64.00m linear /dev/loop1(0) $ lvconvert --type cache --cachepool fast vg/main $ lvs -a vg LV VG Attr LSize Origin Pool Type Devices [fast] vg Cwi---C--- 64.00m linear /dev/loop1(0) main vg Cwi---C--- 128.00m [main_corig] [fast] cache main_corig(0) [main_corig] vg owi---C--- 128.00m linear /dev/loop0(0) $ lvchange -ay vg/main $ dmsetup ls vg-fast_cdata (253:4) vg-fast_cmeta (253:5) vg-main_corig (253:6) vg-main (253:24) vg-fast (253:3) $ dmsetup table vg-fast_cdata: 0 98304 linear 253:3 32768 vg-fast_cmeta: 0 32768 linear 253:3 0 vg-main_corig: 0 262144 linear 7:0 2048 vg-main: 0 262144 cache 253:5 253:4 253:6 128 2 metadata2 writethrough mq 0 vg-fast: 0 131072 linear 7:1 2048 $ lvchange -an vg/min $ lvconvert --splitcache vg/main $ lvs -a vg LV VG Attr LSize Type Devices fast vg -wi------- 64.00m linear /dev/loop1(0) main vg -wi------- 128.00m linear /dev/loop0(0)
2018-08-17 23:45:52 +03:00
size = lv_metadata_size(lv);
return _size64_disp(rh, mem, field, &size, private);
}
if (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) {
size = lv_metadata_size(lv);
return _size64_disp(rh, mem, field, &size, private);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _thincount_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
uint32_t count;
if (seg_is_thin_pool(seg)) {
count = dm_list_size(&seg->lv->segs_using_this_lv);
return _uint32_disp(rh, mem, field, &count, private);
}
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _lvtime_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
uint64_t *sortval;
if (!(repstr = lv_creation_time_dup(mem, lv, 0)) ||
!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
log_error("Failed to allocate buffer for time.");
return 0;
}
*sortval = lv_is_historical(lv) ? lv->this_glv->historical->timestamp : lv->timestamp;
return _field_set_value(field, repstr, sortval);
}
static int _lvtimeremoved_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
uint64_t *sortval;
if (!(repstr = lv_removal_time_dup(mem, lv, 0)) ||
!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
log_error("Failed to allocate buffer for time.");
return 0;
}
*sortval = lv_is_historical(lv) ? lv->this_glv->historical->timestamp_removed : 0;
return _field_set_value(field, repstr, sortval);
}
static int _lvhost_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
char *repstr;
if (!(repstr = lv_host_dup(mem, lv))) {
log_error("Failed to allocate buffer for host.");
return 0;
}
return _field_set_value(field, repstr, NULL);
}
2014-07-02 13:09:14 +04:00
/* PV/VG/LV Attributes */
static int _pvallocatable_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int allocatable = (((const struct physical_volume *) data)->status & ALLOCATABLE_PV) != 0;
return _binary_disp(rh, mem, field, allocatable, GET_FIRST_RESERVED_NAME(pv_allocatable_y), private);
2014-07-02 13:09:14 +04:00
}
static int _pvexported_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int exported = (((const struct physical_volume *) data)->status & EXPORTED_VG) != 0;
return _binary_disp(rh, mem, field, exported, GET_FIRST_RESERVED_NAME(pv_exported_y), private);
2014-07-02 13:09:14 +04:00
}
static int _pvmissing_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int missing = (((const struct physical_volume *) data)->status & MISSING_PV) != 0;
return _binary_disp(rh, mem, field, missing, GET_FIRST_RESERVED_NAME(pv_missing_y), private);
2014-07-02 13:09:14 +04:00
}
static int _pvinuse_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv = (const struct physical_volume *) data;
int used = is_used_pv(pv);
if (used < 0)
return _binary_undef_disp(rh, mem, field, private);
return _binary_disp(rh, mem, field, used, GET_FIRST_RESERVED_NAME(pv_in_use_y), private);
}
static int _pvduplicate_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv = (const struct physical_volume *) data;
int duplicate = lvmcache_dev_is_unused_duplicate(pv->dev);
return _binary_disp(rh, mem, field, duplicate, GET_FIRST_RESERVED_NAME(pv_duplicate_y), private);
}
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
static int _pvdeviceid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv = (const struct physical_volume *) data;
char *repstr;
if (!pv->device_id)
return _field_set_value(field, "", NULL);
if (pv->dev && pv_device_id_is_stale(pv))
return _field_set_value(field, "invalid", NULL);
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
if (!(repstr = pv_deviceid_dup(mem, pv))) {
log_error("Failed to allocate buffer.");
return 0;
}
return _field_set_value(field, repstr, NULL);
}
static int _pvdeviceidtype_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct physical_volume *pv = (const struct physical_volume *) data;
char *repstr;
if (!pv->device_id_type)
return _field_set_value(field, "", NULL);
if (pv->dev && pv_device_id_is_stale(pv))
return _field_set_value(field, "invalid", NULL);
device usage based on devices file The LVM devices file lists devices that lvm can use. The default file is /etc/lvm/devices/system.devices, and the lvmdevices(8) command is used to add or remove device entries. If the file does not exist, or if lvm.conf includes use_devicesfile=0, then lvm will not use a devices file. When the devices file is in use, the regex filter is not used, and the filter settings in lvm.conf or on the command line are ignored. LVM records devices in the devices file using hardware-specific IDs, such as the WWID, and attempts to use subsystem-specific IDs for virtual device types. These device IDs are also written in the VG metadata. When no hardware or virtual ID is available, lvm falls back using the unstable device name as the device ID. When devnames are used, lvm performs extra scanning to find devices if their devname changes, e.g. after reboot. When proper device IDs are used, an lvm command will not look at devices outside the devices file, but when devnames are used as a fallback, lvm will scan devices outside the devices file to locate PVs on renamed devices. A config setting search_for_devnames can be used to control the scanning for renamed devname entries. Related to the devices file, the new command option --devices <devnames> allows a list of devices to be specified for the command to use, overriding the devices file. The listed devices act as a sort of devices file in terms of limiting which devices lvm will see and use. Devices that are not listed will appear to be missing to the lvm command. Multiple devices files can be kept in /etc/lvm/devices, which allows lvm to be used with different sets of devices, e.g. system devices do not need to be exposed to a specific application, and the application can use lvm on its own set of devices that are not exposed to the system. The option --devicesfile <filename> is used to select the devices file to use with the command. Without the option set, the default system devices file is used. Setting --devicesfile "" causes lvm to not use a devices file. An existing, empty devices file means lvm will see no devices. The new command vgimportdevices adds PVs from a VG to the devices file and updates the VG metadata to include the device IDs. vgimportdevices -a will import all VGs into the system devices file. LVM commands run by dmeventd not use a devices file by default, and will look at all devices on the system. A devices file can be created for dmeventd (/etc/lvm/devices/dmeventd.devices) If this file exists, lvm commands run by dmeventd will use it. Internal implementaion: - device_ids_read - read the devices file . add struct dev_use (du) to cmd->use_devices for each devices file entry - dev_cache_scan - get /dev entries . add struct device (dev) to dev_cache for each device on the system - device_ids_match - match devices file entries to /dev entries . match each du on cmd->use_devices to a dev in dev_cache, using device ID . on match, set du->dev, dev->id, dev->flags MATCHED_USE_ID - label_scan - read lvm headers and metadata from devices . filters are applied, those that do not need data from the device . filter-deviceid skips devs without MATCHED_USE_ID, i.e. skips /dev entries that are not listed in the devices file . read lvm label from dev . filters are applied, those that use data from the device . read lvm metadata from dev . add info/vginfo structs for PVs/VGs (info is "lvmcache") - device_ids_find_renamed_devs - handle devices with unstable devname ID where devname changed . this step only needed when devs do not have proper device IDs, and their dev names change, e.g. after reboot sdb becomes sdc. . detect incorrect match because PVID in the devices file entry does not match the PVID found when the device was read above . undo incorrect match between du and dev above . search system devices for new location of PVID . update devices file with new devnames for PVIDs on renamed devices . label_scan the renamed devs - continue with command processing
2020-06-23 21:25:41 +03:00
if (!(repstr = pv_deviceidtype_dup(mem, pv))) {
log_error("Failed to allocate buffer.");
return 0;
}
return _field_set_value(field, repstr, NULL);
}
2014-07-02 13:09:14 +04:00
static int _vgpermissions_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const char *perms = ((const struct volume_group *) data)->status & LVM_WRITE ? GET_FIRST_RESERVED_NAME(vg_permissions_rw)
: GET_FIRST_RESERVED_NAME(vg_permissions_r);
return _field_string(rh, field, perms);
2014-07-02 13:09:14 +04:00
}
static int _vgextendable_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int extendable = (vg_is_resizeable((const struct volume_group *) data)) != 0;
return _binary_disp(rh, mem, field, extendable, GET_FIRST_RESERVED_NAME(vg_extendable_y),private);
2014-07-02 13:09:14 +04:00
}
static int _vgexported_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int exported = (vg_is_exported((const struct volume_group *) data)) != 0;
return _binary_disp(rh, mem, field, exported, GET_FIRST_RESERVED_NAME(vg_exported_y), private);
2014-07-02 13:09:14 +04:00
}
Add metadata-based autoactivation property for VG and LV The autoactivation property can be specified in lvcreate or vgcreate for new LVs/VGs, and the property can be changed by lvchange or vgchange for existing LVs/VGs. --setautoactivation y|n enables|disables autoactivation of a VG or LV. Autoactivation is enabled by default, which is consistent with past behavior. The disabled state is stored as a new flag in the VG metadata, and the absence of the flag allows autoactivation. If autoactivation is disabled for the VG, then no LVs in the VG will be autoactivated (the LV autoactivation property will have no effect.) When autoactivation is enabled for the VG, then autoactivation can be controlled on individual LVs. The state of this property can be reported for LVs/VGs using the "-o autoactivation" option in lvs/vgs commands, which will report "enabled", or "" for the disabled state. Previous versions of lvm do not recognize this property. Since autoactivation is enabled by default, the disabled setting will have no effect in older lvm versions. If the VG is modified by older lvm versions, the disabled state will also be dropped from the metadata. The autoactivation property is an alternative to using the lvm.conf auto_activation_volume_list, which is still applied to to VGs/LVs in addition to the new property. If VG or LV autoactivation is disabled either in metadata or in auto_activation_volume_list, it will not be autoactivated. An autoactivation command will silently skip activating an LV when the autoactivation property is disabled. To determine the effective autoactivation behavior for a specific LV, multiple settings would need to be checked: the VG autoactivation property, the LV autoactivation property, the auto_activation_volume_list. The "activation skip" property would also be relevant, since it applies to both normal and auto activation.
2021-04-02 01:20:00 +03:00
static int _vgautoactivation_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct volume_group *vg = (const struct volume_group *)data;
int aa_yes = (vg->status & NOAUTOACTIVATE) ? 0 : 1;
return _binary_disp(rh, mem, field, aa_yes, "enabled", private);
}
2014-07-02 13:09:14 +04:00
static int _vgpartial_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int partial = (vg_missing_pv_count((const struct volume_group *) data)) != 0;
return _binary_disp(rh, mem, field, partial, GET_FIRST_RESERVED_NAME(vg_partial_y), private);
2014-07-02 13:09:14 +04:00
}
static int _vgallocationpolicy_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const char *alloc_policy = get_alloc_string(((const struct volume_group *) data)->alloc) ? : _str_unknown;
return _field_string(rh, field, alloc_policy);
2014-07-02 13:09:14 +04:00
}
static int _vgclustered_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int clustered = (vg_is_clustered((const struct volume_group *) data)) != 0;
return _binary_disp(rh, mem, field, clustered, GET_FIRST_RESERVED_NAME(vg_clustered_y), private);
2014-07-02 13:09:14 +04:00
}
static int _vgshared_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int shared = (vg_is_shared((const struct volume_group *) data)) != 0;
return _binary_disp(rh, mem, field, shared, GET_FIRST_RESERVED_NAME(vg_shared_y), private);
}
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
static int _lvlayout_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
struct dm_list *lv_layout;
struct dm_list *lv_role;
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
if (!lv_layout_and_role(mem, lv, &lv_layout, &lv_role)) {
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
log_error("Failed to display layout for LV %s/%s.", lv->vg->name, lv->name);
return 0;
}
return _field_set_string_list(rh, field, lv_layout, private, 0, NULL);
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
}
static int _lvrole_disp(struct dm_report *rh, struct dm_pool *mem,
2014-07-02 13:09:14 +04:00
struct dm_report_field *field,
const void *data, void *private)
{
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
const struct logical_volume *lv = (const struct logical_volume *) data;
struct dm_list *lv_layout;
struct dm_list *lv_role;
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
if (!lv_layout_and_role(mem, lv, &lv_layout, &lv_role)) {
log_error("Failed to display role for LV %s/%s.", lv->vg->name, lv->name);
Add lv_layout_and_type fn, lv_layout and lv_type reporting fields. The lv_layout and lv_type fields together help with LV identification. We can do basic identification using the lv_attr field which provides very condensed view. In contrast to that, the new lv_layout and lv_type fields provide more detialed information on exact layout and type used for LVs. For top-level LVs which are pure types not combined with any other LV types, the lv_layout value is equal to lv_type value. For non-top-level LVs which may be combined with other types, the lv_layout describes the underlying layout used, while the lv_type describes the use/type/usage of the LV. These two new fields are both string lists so selection (-S/--select) criteria can be defined using the list operators easily: [] for strict matching {} for subset matching. For example, let's consider this: $ lvs -a -o name,vg_name,lv_attr,layout,type LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare pool vg twi-a-tz-- pool,thin pool,thin [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tdata_rimage_0] vg iwi-aor--- linear image,raid [pool_tdata_rimage_1] vg iwi-aor--- linear image,raid [pool_tdata_rimage_2] vg iwi-aor--- linear image,raid [pool_tdata_rimage_3] vg iwi-aor--- linear image,raid [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rimage_0] vg iwi-aor--- linear image,raid [pool_tmeta_rimage_1] vg iwi-aor--- linear image,raid [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin thin_vol1 vg Vwi-a-tz-- thin thin thin_vol2 vg Vwi-a-tz-- thin multiple,origin,thin Which is a situation with thin pool, thin volumes and thin snapshots. We can see internal 'pool_tdata' volume that makes up thin pool has actually a level10 raid layout and the internal 'pool_tmeta' has level1 raid layout. Also, we can see that 'thin_snap1' and 'thin_snap2' are both thin snapshots while 'thin_vol1' is thin origin (having multiple snapshots). Such reporting scheme provides much better base for selection criteria in addition to providing more detailed information, for example: $ lvs -a -o name,vg_name,lv_attr,layout,type -S 'type=metadata' LV VG Attr Layout Type [lvol1_pmspare] vg ewi------- linear metadata,pool,spare [pool_tdata_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_1] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_2] vg ewi-aor--- linear metadata,raid [pool_tdata_rmeta_3] vg ewi-aor--- linear metadata,raid [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin [pool_tmeta_rmeta_0] vg ewi-aor--- linear metadata,raid [pool_tmeta_rmeta_1] vg ewi-aor--- linear metadata,raid (selected all LVs which are related to metadata of any type) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={metadata,thin}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs which hold metadata related to thin) lvs -a -o name,vg_name,lv_attr,layout,type -S 'type={thin,snapshot}' LV VG Attr Layout Type thin_snap1 vg Vwi---tz-k thin snapshot,thin thin_snap2 vg Vwi---tz-k thin snapshot,thin (selected all LVs which are thin snapshots) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout=raid' LV VG Attr Layout Type [pool_tdata] vg rwi-aor--- level10,raid data,pool,thin [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid layout, any raid layout) lvs -a -o name,vg_name,lv_attr,layout,type -S 'layout={raid,level1}' LV VG Attr Layout Type [pool_tmeta] vg ewi-aor--- level1,raid metadata,pool,thin (selected all LVs with raid level1 layout exactly) And so on...
2014-08-13 12:03:45 +04:00
return 0;
}
return _field_set_string_list(rh, field, lv_role, private, 0, NULL);
2014-07-02 13:09:14 +04:00
}
static int _lvinitialimagesync_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int initial_image_sync;
if (lv_is_raid(lv) || lv_is_mirrored(lv))
2016-07-14 16:21:01 +03:00
initial_image_sync = !lv_is_not_synced(lv);
2014-07-02 13:09:14 +04:00
else
initial_image_sync = 0;
return _binary_disp(rh, mem, field, initial_image_sync, GET_FIRST_RESERVED_NAME(lv_initial_image_sync_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvimagesynced_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int image_synced;
if (lv_is_raid_image(lv))
image_synced = !lv_is_visible(lv) && lv_raid_image_in_sync(lv);
else if (lv_is_mirror_image(lv))
image_synced = lv_mirror_image_in_sync(lv);
else
image_synced = 0;
return _binary_disp(rh, mem, field, image_synced, GET_FIRST_RESERVED_NAME(lv_image_synced_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvmerging_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int merging;
if (lv_is_origin(lv) || lv_is_external_origin(lv))
merging = lv_is_merging_origin(lv);
else if (lv_is_cow(lv))
merging = lv_is_merging_cow(lv);
else if (lv_is_thin_volume(lv))
merging = lv_is_merging_thin_snapshot(lv);
else
merging = 0;
return _binary_disp(rh, mem, field, merging, GET_FIRST_RESERVED_NAME(lv_merging_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvconverting_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int converting = lv_is_converting((const struct logical_volume *) data);
2014-07-02 13:09:14 +04:00
return _binary_disp(rh, mem, field, converting, "converting", private);
}
static int _lvpermissions_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
const char *perms = "";
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
if (!lv_is_pvmove(lvdm->lv)) {
if (lvdm->lv->status & LVM_WRITE) {
if (!lvdm->info.exists)
perms = _str_unknown;
else if (lvdm->info.read_only)
perms = GET_FIRST_RESERVED_NAME(lv_permissions_r_override);
2014-07-02 13:09:14 +04:00
else
perms = GET_FIRST_RESERVED_NAME(lv_permissions_rw);
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
} else if (lvdm->lv->status & LVM_READ)
perms = GET_FIRST_RESERVED_NAME(lv_permissions_r);
2014-07-02 13:09:14 +04:00
else
perms = _str_unknown;
}
return _field_string(rh, field, perms);
2014-07-02 13:09:14 +04:00
}
static int _lvallocationpolicy_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const char *alloc_policy = get_alloc_string(((const struct logical_volume *) data)->alloc) ? : _str_unknown;
return _field_string(rh, field, alloc_policy);
2014-07-02 13:09:14 +04:00
}
static int _lvallocationlocked_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int alloc_locked = (((const struct logical_volume *) data)->status & LOCKED) != 0;
return _binary_disp(rh, mem, field, alloc_locked, GET_FIRST_RESERVED_NAME(lv_allocation_locked_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvfixedminor_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int fixed_minor = (((const struct logical_volume *) data)->status & FIXED_MINOR) != 0;
return _binary_disp(rh, mem, field, fixed_minor, GET_FIRST_RESERVED_NAME(lv_fixed_minor_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvactive_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int active;
if (!activation())
return _binary_undef_disp(rh, mem, field, private);
active = lv_is_active(lv);
return _binary_disp(rh, mem, field, active, GET_FIRST_RESERVED_NAME(lv_active_y), private);
}
static int _lvactivelocally_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int active_locally;
if (!activation())
return _binary_undef_disp(rh, mem, field, private);
active_locally = lv_is_active(lv);
return _binary_disp(rh, mem, field, active_locally, GET_FIRST_RESERVED_NAME(lv_active_locally_y), private);
}
static int _lvactiveremotely_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int active_remotely;
if (!activation() || vg_is_shared(lv->vg))
return _binary_undef_disp(rh, mem, field, private);
active_remotely = 0;
return _binary_disp(rh, mem, field, active_remotely, GET_FIRST_RESERVED_NAME(lv_active_remotely_y), private);
}
static int _lvactiveexclusively_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
int ex = 0, sh = 0;
if (!activation())
return _binary_undef_disp(rh, mem, field, private);
ex = lv_is_active(lv);
if (ex && vg_is_shared(lv->vg)) {
ex = 0;
if (!lockd_query_lv(lv->vg->cmd, (struct logical_volume *)lv, &ex, &sh))
return _binary_undef_disp(rh, mem, field, private);
}
return _binary_disp(rh, mem, field, ex, GET_FIRST_RESERVED_NAME(lv_active_exclusively_y), private);
}
2014-07-02 13:09:14 +04:00
static int _lvmergefailed_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->seg_status.type != SEG_STATUS_SNAPSHOT)
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
return _binary_disp(rh, mem, field, lvdm->seg_status.snapshot->merge_failed,
GET_FIRST_RESERVED_NAME(lv_merge_failed_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvsnapshotinvalid_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->seg_status.type != SEG_STATUS_SNAPSHOT)
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
return _binary_disp(rh, mem, field, lvdm->seg_status.snapshot->invalid,
GET_FIRST_RESERVED_NAME(lv_snapshot_invalid_y), private);
2014-07-02 13:09:14 +04:00
}
static int _lvsuspended_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->info.exists)
return _binary_disp(rh, mem, field, lvdm->info.suspended, GET_FIRST_RESERVED_NAME(lv_suspended_y), private);
2014-07-02 13:09:14 +04:00
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
}
static int _lvlivetable_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->info.exists)
return _binary_disp(rh, mem, field, lvdm->info.live_table, GET_FIRST_RESERVED_NAME(lv_live_table_y), private);
2014-07-02 13:09:14 +04:00
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
}
static int _lvinactivetable_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->info.exists)
return _binary_disp(rh, mem, field, lvdm->info.inactive_table, GET_FIRST_RESERVED_NAME(lv_inactive_table_y), private);
2014-07-02 13:09:14 +04:00
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
}
static int _lvdeviceopen_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
2014-07-02 13:09:14 +04:00
if (lvdm->info.exists)
return _binary_disp(rh, mem, field, lvdm->info.open_count, GET_FIRST_RESERVED_NAME(lv_device_open_y), private);
2014-07-02 13:09:14 +04:00
return _binary_undef_disp(rh, mem, field, private);
2014-07-02 13:09:14 +04:00
}
static int _thinzero_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_thin_volume(seg))
seg = first_seg(seg->pool_lv);
if (seg_is_thin_pool(seg))
return _binary_disp(rh, mem, field, (seg->zero_new_blocks == THIN_ZERO_YES), GET_FIRST_RESERVED_NAME(zero_y), private);
return _binary_undef_disp(rh, mem, field, private);
}
2014-07-02 13:09:14 +04:00
static int _lvhealthstatus_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
const struct logical_volume *lv = lvdm->lv;
2014-07-02 13:09:14 +04:00
const char *health = "";
uint64_t n;
if (lv_is_partial(lv))
2014-07-02 13:09:14 +04:00
health = "partial";
else if (lv_is_raid_type(lv)) {
if (!activation())
health = "unknown";
else if (!lv_raid_healthy(lv))
health = "refresh needed";
else if (lv_is_raid(lv)) {
if (lv_raid_mismatch_count(lv, &n) && n)
health = "mismatches exist";
} else if (lv->status & LV_WRITEMOSTLY)
health = "writemostly";
} else if (lv_is_cache(lv) && (lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type != SEG_STATUS_CACHE)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(health_undef),
GET_FIELD_RESERVED_VALUE(health_undef));
if (lvdm->seg_status.cache->fail)
health = "failed";
else if (lvdm->seg_status.cache->read_only)
health = "metadata_read_only";
} else if (lv_is_writecache(lv) && (lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type != SEG_STATUS_WRITECACHE)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(health_undef),
GET_FIELD_RESERVED_VALUE(health_undef));
if (lvdm->seg_status.writecache->error)
health = "error";
} else if (lv_is_thin_pool(lv) && (lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type != SEG_STATUS_THIN_POOL)
return _field_set_value(field, GET_FIRST_RESERVED_NAME(health_undef),
GET_FIELD_RESERVED_VALUE(health_undef));
if (lvdm->seg_status.thin_pool->fail)
health = "failed";
else if (lvdm->seg_status.thin_pool->out_of_data_space)
health = "out_of_data";
else if (lvdm->seg_status.thin_pool->read_only)
health = "metadata_read_only";
2014-07-02 13:09:14 +04:00
}
return _field_string(rh, field, health);
2014-07-02 13:09:14 +04:00
}
static int _lvcheckneeded_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data;
if (lv_is_thin_pool(lvdm->lv) && lvdm->seg_status.type == SEG_STATUS_THIN_POOL)
return _binary_disp(rh, mem, field, lvdm->seg_status.thin_pool->needs_check,
GET_FIRST_RESERVED_NAME(lv_check_needed_y), private);
if (lv_is_cache(lvdm->lv) && lvdm->seg_status.type == SEG_STATUS_CACHE)
return _binary_disp(rh, mem, field, lvdm->seg_status.cache->needs_check,
GET_FIRST_RESERVED_NAME(lv_check_needed_y), private);
return _binary_undef_disp(rh, mem, field, private);
}
2014-07-02 13:09:14 +04:00
static int _lvskipactivation_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int skip_activation = (((const struct logical_volume *) data)->status & LV_ACTIVATION_SKIP) != 0;
return _binary_disp(rh, mem, field, skip_activation, "skip activation", private);
}
Add metadata-based autoactivation property for VG and LV The autoactivation property can be specified in lvcreate or vgcreate for new LVs/VGs, and the property can be changed by lvchange or vgchange for existing LVs/VGs. --setautoactivation y|n enables|disables autoactivation of a VG or LV. Autoactivation is enabled by default, which is consistent with past behavior. The disabled state is stored as a new flag in the VG metadata, and the absence of the flag allows autoactivation. If autoactivation is disabled for the VG, then no LVs in the VG will be autoactivated (the LV autoactivation property will have no effect.) When autoactivation is enabled for the VG, then autoactivation can be controlled on individual LVs. The state of this property can be reported for LVs/VGs using the "-o autoactivation" option in lvs/vgs commands, which will report "enabled", or "" for the disabled state. Previous versions of lvm do not recognize this property. Since autoactivation is enabled by default, the disabled setting will have no effect in older lvm versions. If the VG is modified by older lvm versions, the disabled state will also be dropped from the metadata. The autoactivation property is an alternative to using the lvm.conf auto_activation_volume_list, which is still applied to to VGs/LVs in addition to the new property. If VG or LV autoactivation is disabled either in metadata or in auto_activation_volume_list, it will not be autoactivated. An autoactivation command will silently skip activating an LV when the autoactivation property is disabled. To determine the effective autoactivation behavior for a specific LV, multiple settings would need to be checked: the VG autoactivation property, the LV autoactivation property, the auto_activation_volume_list. The "activation skip" property would also be relevant, since it applies to both normal and auto activation.
2021-04-02 01:20:00 +03:00
static int _lvautoactivation_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
int aa_yes = (((const struct logical_volume *) data)->status & LV_NOAUTOACTIVATE) ? 0 : 1;
return _binary_disp(rh, mem, field, aa_yes, "enabled", private);
}
static int _lvhistorical_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
return _binary_disp(rh, mem, field, lv_is_historical(lv), "historical", private);
}
/*
* Macro to generate '_cache_<cache_status_field_name>_disp' reporting function.
* The 'cache_status_field_name' is field name from struct dm_cache_status.
*/
#define GENERATE_CACHE_STATUS_DISP_FN(cache_status_field_name) \
static int _cache_ ## cache_status_field_name ## _disp (struct dm_report *rh, \
struct dm_pool *mem, \
struct dm_report_field *field, \
const void *data, \
void *private) \
{ \
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data; \
if (lvdm->seg_status.type != SEG_STATUS_CACHE) \
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
return dm_report_field_uint64(rh, field, &lvdm->seg_status.cache->cache_status_field_name); \
}
GENERATE_CACHE_STATUS_DISP_FN(total_blocks)
GENERATE_CACHE_STATUS_DISP_FN(used_blocks)
GENERATE_CACHE_STATUS_DISP_FN(dirty_blocks)
GENERATE_CACHE_STATUS_DISP_FN(read_hits)
GENERATE_CACHE_STATUS_DISP_FN(read_misses)
GENERATE_CACHE_STATUS_DISP_FN(write_hits)
GENERATE_CACHE_STATUS_DISP_FN(write_misses)
/*
* Macro to generate '_writecache_<cache_status_field_name>_disp' reporting function.
* The 'writecache_status_field_name' is field name from struct dm_writecache_status.
*/
#define GENERATE_WRITECACHE_STATUS_DISP_FN(writecache_status_field_name) \
static int _writecache_ ## writecache_status_field_name ## _disp (struct dm_report *rh, \
struct dm_pool *mem, \
struct dm_report_field *field, \
const void *data, \
void *private) \
{ \
const struct lv_with_info_and_seg_status *lvdm = (const struct lv_with_info_and_seg_status *) data; \
if (lvdm->seg_status.type != SEG_STATUS_WRITECACHE) \
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
return dm_report_field_uint64(rh, field, &lvdm->seg_status.writecache->writecache_status_field_name); \
}
GENERATE_WRITECACHE_STATUS_DISP_FN(total_blocks)
GENERATE_WRITECACHE_STATUS_DISP_FN(free_blocks)
GENERATE_WRITECACHE_STATUS_DISP_FN(writeback_blocks)
GENERATE_WRITECACHE_STATUS_DISP_FN(error)
vdo: add lvs fields to query vdo volume properties Add lots of vdo fields: vdo_operating_mode - For vdo pools, its current operating mode. vdo_compression_state - For vdo pools, whether compression is running. vdo_index_state - For vdo pools, state of index for deduplication. vdo_used_size - For vdo pools, currently used space. vdo_saving_percent - For vdo pools, percentage of saved space. vdo_compression - Set for compressed LV (vdopool). vdo_deduplication - Set for deduplicated LV (vdopool). vdo_use_metadata_hints - Use REQ_SYNC for writes (vdopool). vdo_minimum_io_size - Minimum acceptable IO size (vdopool). vdo_block_map_cache_size - Allocated caching size (vdopool). vdo_block_map_era_length - Speed of cache writes (vdopool). vdo_use_sparse_index - Sparse indexing (vdopool). vdo_index_memory_size - Allocated indexing memory (vdopool). vdo_slab_size - Increment size for growing (vdopool). vdo_ack_threads - Acknowledging threads (vdopool). vdo_bio_threads - IO submitting threads (vdopool). vdo_bio_rotation - IO enqueue (vdopool). vdo_cpu_threads - CPU threads for compression and hashing (vdopool). vdo_hash_zone_threads - Threads for subdivide parts (vdopool). vdo_logical_threads - Logical threads for subdivide parts (vdopool). vdo_physical_threads - Physical threads for subdivide parts (vdopool). vdo_max_discard - Maximum discard size volume can recieve (vdopool). vdo_write_policy - Specified write policy (vdopool). vdo_header_size - Header size at front of vdopool. Previously only 'lvdisplay -m' was exposing them.
2019-10-04 18:02:20 +03:00
/*
* Macro to generate '_vdo_<vdo_field_name>_disp' reporting function.
* The 'vdo_field_name' is field name from struct lv_vdo_status.
*/
#define GENERATE_VDO_FIELD_DISP_FN(vdo_field_name) \
static int _vdo_ ## vdo_field_name ## _disp (struct dm_report *rh, struct dm_pool *mem, \
struct dm_report_field *field, \
const void *data, void *private) \
{ \
const struct lv_segment *seg = (const struct lv_segment *) data; \
\
if (seg_is_vdo(seg)) \
seg = first_seg(seg_lv(seg, 0)); \
\
if (!seg_is_vdo_pool(seg)) \
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
\
return dm_report_field_uint32(rh, field, &seg->vdo_params.vdo_field_name); \
}
GENERATE_VDO_FIELD_DISP_FN(block_map_era_length)
GENERATE_VDO_FIELD_DISP_FN(ack_threads)
GENERATE_VDO_FIELD_DISP_FN(bio_threads)
GENERATE_VDO_FIELD_DISP_FN(bio_rotation)
GENERATE_VDO_FIELD_DISP_FN(cpu_threads)
GENERATE_VDO_FIELD_DISP_FN(hash_zone_threads)
GENERATE_VDO_FIELD_DISP_FN(logical_threads)
GENERATE_VDO_FIELD_DISP_FN(physical_threads)
GENERATE_VDO_FIELD_DISP_FN(max_discard)
/*
* Macro to generate '_vdo_<vdo_field_name>_disp' reporting function.
* The 'vdo_field_name' is field name from struct lv_vdo_status.
*/
#define GENERATE_VDO_FIELDSZMB_DISP_FN(vdo_field_name) \
static int _vdo_ ## vdo_field_name ## _disp (struct dm_report *rh, struct dm_pool *mem, \
struct dm_report_field *field, \
const void *data, void *private) \
{ \
uint64_t size; \
const struct lv_segment *seg = (const struct lv_segment *) data; \
\
if (seg_is_vdo(seg)) \
seg = first_seg(seg_lv(seg, 0)); \
\
if (!seg_is_vdo_pool(seg)) \
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
\
size = seg->vdo_params.vdo_field_name ## _mb * (UINT64_C(1024) * 1024 >> SECTOR_SHIFT); \
vdo: add lvs fields to query vdo volume properties Add lots of vdo fields: vdo_operating_mode - For vdo pools, its current operating mode. vdo_compression_state - For vdo pools, whether compression is running. vdo_index_state - For vdo pools, state of index for deduplication. vdo_used_size - For vdo pools, currently used space. vdo_saving_percent - For vdo pools, percentage of saved space. vdo_compression - Set for compressed LV (vdopool). vdo_deduplication - Set for deduplicated LV (vdopool). vdo_use_metadata_hints - Use REQ_SYNC for writes (vdopool). vdo_minimum_io_size - Minimum acceptable IO size (vdopool). vdo_block_map_cache_size - Allocated caching size (vdopool). vdo_block_map_era_length - Speed of cache writes (vdopool). vdo_use_sparse_index - Sparse indexing (vdopool). vdo_index_memory_size - Allocated indexing memory (vdopool). vdo_slab_size - Increment size for growing (vdopool). vdo_ack_threads - Acknowledging threads (vdopool). vdo_bio_threads - IO submitting threads (vdopool). vdo_bio_rotation - IO enqueue (vdopool). vdo_cpu_threads - CPU threads for compression and hashing (vdopool). vdo_hash_zone_threads - Threads for subdivide parts (vdopool). vdo_logical_threads - Logical threads for subdivide parts (vdopool). vdo_physical_threads - Physical threads for subdivide parts (vdopool). vdo_max_discard - Maximum discard size volume can recieve (vdopool). vdo_write_policy - Specified write policy (vdopool). vdo_header_size - Header size at front of vdopool. Previously only 'lvdisplay -m' was exposing them.
2019-10-04 18:02:20 +03:00
\
return _size64_disp(rh, mem, field, &size, private);\
}
GENERATE_VDO_FIELDSZMB_DISP_FN(block_map_cache_size)
GENERATE_VDO_FIELDSZMB_DISP_FN(index_memory_size)
GENERATE_VDO_FIELDSZMB_DISP_FN(slab_size)
static int _vdo_compression_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data; \
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _binary_disp(rh, mem, field, seg->vdo_params.use_compression,
GET_FIRST_RESERVED_NAME(vdo_compression_y), private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
}
static int _vdo_deduplication_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data; \
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _binary_disp(rh, mem, field, seg->vdo_params.use_deduplication,
GET_FIRST_RESERVED_NAME(vdo_deduplication_y), private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64)); \
}
static int _vdo_use_metadata_hints_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _binary_disp(rh, mem, field, seg->vdo_params.use_metadata_hints,
GET_FIRST_RESERVED_NAME(vdo_use_metadata_hints_y),
private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_use_sparse_index_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _binary_disp(rh, mem, field, seg->vdo_params.use_sparse_index,
GET_FIRST_RESERVED_NAME(vdo_use_sparse_index_y),
private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_minimum_io_size_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _size32_disp(rh, mem, field, &seg->vdo_params.minimum_io_size, private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_header_size_disp(struct dm_report *rh,
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _size32_disp(rh, mem, field, &seg->vdo_pool_header_size, private);
return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
}
static int _vdo_write_policy_disp(struct dm_report *rh,
struct dm_pool *mem,
struct dm_report_field *field,
const void *data,
void *private)
{
const struct lv_segment *seg = (const struct lv_segment *) data;
if (seg_is_vdo(seg))
seg = first_seg(seg_lv(seg, 0));
if (seg_is_vdo_pool(seg))
return _field_string(rh, field, get_vdo_write_policy_name(seg->vdo_params.write_policy));
return _field_set_value(field, GET_FIRST_RESERVED_NAME(vdo_write_policy_undef),
GET_FIELD_RESERVED_VALUE(vdo_write_policy_undef));
}
/* Report object types */
/* necessary for displaying something for PVs not belonging to VG */
static struct format_instance _dummy_fid = {
.metadata_areas_in_use = DM_LIST_HEAD_INIT(_dummy_fid.metadata_areas_in_use),
.metadata_areas_ignored = DM_LIST_HEAD_INIT(_dummy_fid.metadata_areas_ignored),
};
static struct volume_group _dummy_vg = {
.fid = &_dummy_fid,
.name = "",
.system_id = (char *) "",
.pvs = DM_LIST_HEAD_INIT(_dummy_vg.pvs),
.lvs = DM_LIST_HEAD_INIT(_dummy_vg.lvs),
2016-03-01 17:18:42 +03:00
.historical_lvs = DM_LIST_HEAD_INIT(_dummy_vg.historical_lvs),
.tags = DM_LIST_HEAD_INIT(_dummy_vg.tags),
};
static struct volume_group _unknown_vg = {
.fid = &_dummy_fid,
.name = "[unknown]",
.system_id = (char *) "",
.pvs = DM_LIST_HEAD_INIT(_unknown_vg.pvs),
.lvs = DM_LIST_HEAD_INIT(_unknown_vg.lvs),
2016-03-01 17:18:42 +03:00
.historical_lvs = DM_LIST_HEAD_INIT(_unknown_vg.historical_lvs),
.tags = DM_LIST_HEAD_INIT(_unknown_vg.tags),
};
static void *_obj_get_vg(void *obj)
{
struct volume_group *vg = ((struct lvm_report_object *)obj)->vg;
return vg ? vg : &_dummy_vg;
}
static void *_obj_get_lv(void *obj)
{
return (struct logical_volume *)((struct lvm_report_object *)obj)->lvdm->lv;
}
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
static void *_obj_get_lv_with_info_and_seg_status(void *obj)
{
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
return ((struct lvm_report_object *)obj)->lvdm;
}
static void *_obj_get_pv(void *obj)
{
return ((struct lvm_report_object *)obj)->pv;
}
static void *_obj_get_label(void *obj)
{
return ((struct lvm_report_object *)obj)->label;
}
static void *_obj_get_seg(void *obj)
{
return ((struct lvm_report_object *)obj)->seg;
}
static void *_obj_get_pvseg(void *obj)
{
return ((struct lvm_report_object *)obj)->pvseg;
}
static void *_obj_get_devtypes(void *obj)
{
return obj;
}
static void *_obj_get_cmdlog(void *obj)
{
return obj;
}
static const struct dm_report_object_type _log_report_types[] = {
{ CMDLOG, "Command Log", "log_", _obj_get_cmdlog },
{ 0, "", "", NULL },
};
static const struct dm_report_object_type _report_types[] = {
{ VGS, "Volume Group", "vg_", _obj_get_vg },
{ LVS, "Logical Volume", "lv_", _obj_get_lv },
refactor: rename struct lv_with_info used in reporting code to lv_with_info_and_seg_status The former struct lv_with_info is renamed to lv_with_info_and_seg_status as it can hold more than just "info", there's lv's segment status now in addition: struct lv_with_info_and_seg_status { struct logical_volume *lv; struct lvinfo *info; struct lv_seg_status *seg_status; } Where struct lv_seg_status is: struct lv_seg_status { struct dm_pool *mem; struct lv_segment lv_seg; lv_seg_status_type_t type; void *status; /* struct dm_status_* */ } Where lv_seg points to lv's segment that is being reported or processed in general. New struct lv_seg_status keeps the information about segment status - the status retrieved via DM_DEVICE_STATUS ioctl. This information will be used for reporting dm device target status for the LV segment specified. So this patch introduces third level of LV information that is kept for reuse while reporting fields within one reporting line, causing only one DM_DEVICE_STATUS ioctl call per LV segment line reported (otherwise we'd need to call the DM_DEVICE_STATUS for each segment status field in one LV segment/reporting line which is not efficient). This is following exactly the same principle as already introduced by commit ecb2be5d1642aa0142d216f9e52f64fd3e8c3fc8. So currently we have three levels of information that can be used to report an LV/LV segment: - LV metadata itself (struct logical_volume *lv) - LV's DM_DEVICE_INFO ioctl result (struct lvinfo *info) - LV's segment DM_DEVICE_STATUS ioctl result (this status must be bound to a segment, not the whole LV as the whole LV may be composed of several segments of course) (this is the new struct lv_seg_status *seg_status)
2014-10-20 15:46:50 +04:00
{ LVSINFO, "Logical Volume Device Info", "lv_", _obj_get_lv_with_info_and_seg_status },
{ LVSSTATUS, "Logical Volume Device Status", "lv_", _obj_get_lv_with_info_and_seg_status },
{ LVSINFOSTATUS, "Logical Volume Device Info and Status Combined", "lv_", _obj_get_lv_with_info_and_seg_status },
{ PVS, "Physical Volume", "pv_", _obj_get_pv },
{ LABEL, "Physical Volume Label", "pv_", _obj_get_label },
{ SEGS, "Logical Volume Segment", "seg_", _obj_get_seg },
{ PVSEGS, "Physical Volume Segment", "pvseg_", _obj_get_pvseg },
{ 0, "", "", NULL },
};
static const struct dm_report_object_type _devtypes_report_types[] = {
{ DEVTYPES, "Device Types", "devtype_", _obj_get_devtypes },
{ 0, "", "", NULL },
};
/*
* Import column definitions
*/
#define STR DM_REPORT_FIELD_TYPE_STRING
#define NUM DM_REPORT_FIELD_TYPE_NUMBER
2014-07-02 13:09:14 +04:00
#define BIN DM_REPORT_FIELD_TYPE_NUMBER
#define SIZ DM_REPORT_FIELD_TYPE_SIZE
#define PCT DM_REPORT_FIELD_TYPE_PERCENT
report: add support for time (basic) This patch adds support for time values used in reporting fields. The raw values are always stored as number of seconds since epoch. The support that comes with this patch is the basic one which allows only for recognition of strictly formatted date and time in selection criteria (the format follows a subset of formats defined by ISO 8601): date time timezone date: YYYY-MM-DD (or shortly YYYYMMDD) YYYY-MM (shortly YYYYMM), auto DD=1 YYYY, auto MM=01 and DD=01 time: hh:mm:ss (or shortly hhmmss) hh:mm (or shortly hhmm), auto ss=0 hh (or shortly hh), auto mm=0, auto ss=0 timezone (always with + or - sign): +hh:mm or -hh:mm (or shortly +hhmm or -hhmm) +hh or -hh Or directly the time (number of seconds) since Epoch (1970-01-01 00:00:00 UTC) when the number value is prefixed by "@": @number_of_seconds_since_epoch This patch also adds aliases for comparison operators used together with time values which are more intuitive to use: since (as alias for >=) after (as alias for >) until (as alias for <=) before (as alias for <) For example: $ lvmconfig --type full report/time_format time_format="%Y-%m-%d %T %z %Z [%s]" $ lvs -o name,time vg LV Time lvol0 2015-06-28 21:25:41 +0200 CEST [1435519541] lvol1 2015-06-30 03:25:43 +0200 CEST [1435627543] lvol2 2015-04-26 14:52:20 +0200 CEST [1430052740] lvol3 2015-06-30 14:52:23 +0200 CEST [1435668743] $ lvs vg -o name,time -S 'time since "2015-04-26 15:00" && time until "2015-06-30"' LV Time lvol0 2015-06-28 21:25:41 +0200 CEST [1435519541] lvol1 2015-06-30 03:25:43 +0200 CEST [1435627543] lvol3 2015-06-30 14:52:23 +0200 CEST [1435668743] $ lvs vg -o name,time -S 'time since "2015-04-26 15:00" && time until "2015-06-30 6:00"' LV Time lvol0 2015-06-28 21:25:41 +0200 CEST [1435519541] lvol1 2015-06-30 03:25:43 +0200 CEST [1435627543] $ lvs vg -o name,time -S 'time since @1435519541' LV Time lvol0 2015-06-28 21:25:41 +0200 CEST [1435519541] lvol1 2015-06-30 03:25:43 +0200 CEST [1435627543] lvol3 2015-06-30 14:52:23 +0200 CEST [1435668743] This is basic time recognition support that is directly a part of libdevmapper. Recognition of more free-form expressions will be a part of subsequent patches.
2015-05-21 16:19:03 +03:00
#define TIM DM_REPORT_FIELD_TYPE_TIME
#define STR_LIST DM_REPORT_FIELD_TYPE_STRING_LIST
lvm2app: Add signed numerical property values Currently lvm2app properties have the following structure: typedef struct lvm_property_value { uint32_t is_settable:1; uint32_t is_string:1; uint32_t is_integer:1; uint32_t is_valid:1; uint32_t padding:28; union { const char *string; uint64_t integer; } value; } lvm_property_value_t; which assumes that numerical values were in the range of 0 to 2**64-1. However, some of the properties were 'signed', like LV major/minor numbers and some reserved values for properties that represent percentages. Thus when the values were retrieved they were in two's complement notation. So for a -1 major number the API user would get a value of 18446744073709551615. The API user could cast the returned value to an int64_t to handle this, but that requires the API developer to look at the source code and determine when it should be done. This change modifies the return property structure to: typedef struct lvm_property_value { uint32_t is_settable:1; uint32_t is_string:1; uint32_t is_integer:1; uint32_t is_valid:1; uint32_t is_signed:1; uint32_t padding:27; union { const char *string; uint64_t integer; int64_t signed_integer; } value; } lvm_property_value_t; With this addition the API user can interrogate that the value is numerical, (is_integer = 1) and subsequently check if it's signed (is_signed = 1) too. If signed, then the API developer should use the union's signed_integer to avoid casting. This change maintains backwards compatibility as the structure size remains unchanged and integer value remains unchanged. Only the additional bit taken from the pad is utilized. Bugzilla reference: https://bugzilla.redhat.com/show_bug.cgi?id=838257 Signed-off-by: Tony Asleson <tasleson@redhat.com>
2015-05-04 22:51:41 +03:00
#define SNUM DM_REPORT_FIELD_TYPE_NUMBER
#define FIELD(type, strct, sorttype, head, field, width, func, id, desc, writeable) \
2017-07-19 17:17:30 +03:00
{type, sorttype, offsetof(type_ ## strct, field), (width) ? : sizeof(head) - 1, \
#id, head, &_ ## func ## _disp, desc},
typedef struct cmd_log_item type_cmd_log_item;
typedef struct physical_volume type_pv;
typedef struct logical_volume type_lv;
typedef struct volume_group type_vg;
typedef struct lv_segment type_seg;
typedef struct pv_segment type_pvseg;
typedef struct label type_label;
typedef dev_known_type_t type_devtype;
static const struct dm_report_field_type _fields[] = {
/* coverity[unnecessary_header] */
#include "columns.h"
{0, 0, 0, 0, "", "", NULL, NULL},
};
static const struct dm_report_field_type _devtypes_fields[] = {
#include "columns-devtypes.h"
{0, 0, 0, 0, "", "", NULL, NULL},
};
static const struct dm_report_field_type _log_fields[] = {
/* coverity[unnecessary_header] */
#include "columns-cmdlog.h"
{0, 0, 0, 0, "", "", NULL, NULL},
};
#undef STR
#undef NUM
2014-07-02 13:09:14 +04:00
#undef BIN
#undef SIZ
#undef STR_LIST
lvm2app: Add signed numerical property values Currently lvm2app properties have the following structure: typedef struct lvm_property_value { uint32_t is_settable:1; uint32_t is_string:1; uint32_t is_integer:1; uint32_t is_valid:1; uint32_t padding:28; union { const char *string; uint64_t integer; } value; } lvm_property_value_t; which assumes that numerical values were in the range of 0 to 2**64-1. However, some of the properties were 'signed', like LV major/minor numbers and some reserved values for properties that represent percentages. Thus when the values were retrieved they were in two's complement notation. So for a -1 major number the API user would get a value of 18446744073709551615. The API user could cast the returned value to an int64_t to handle this, but that requires the API developer to look at the source code and determine when it should be done. This change modifies the return property structure to: typedef struct lvm_property_value { uint32_t is_settable:1; uint32_t is_string:1; uint32_t is_integer:1; uint32_t is_valid:1; uint32_t is_signed:1; uint32_t padding:27; union { const char *string; uint64_t integer; int64_t signed_integer; } value; } lvm_property_value_t; With this addition the API user can interrogate that the value is numerical, (is_integer = 1) and subsequently check if it's signed (is_signed = 1) too. If signed, then the API developer should use the union's signed_integer to avoid casting. This change maintains backwards compatibility as the structure size remains unchanged and integer value remains unchanged. Only the additional bit taken from the pad is utilized. Bugzilla reference: https://bugzilla.redhat.com/show_bug.cgi?id=838257 Signed-off-by: Tony Asleson <tasleson@redhat.com>
2015-05-04 22:51:41 +03:00
#undef SNUM
#undef FIELD
report_headings_t report_headings_str_to_type(const char *str)
{
if (!str || !*str)
return REPORT_HEADINGS_UNKNOWN;
if (!strcmp(str, "none") || !strcmp(str, "0"))
return REPORT_HEADINGS_NONE;
if (!strcmp(str, "abbrev") || !strcmp(str, "1"))
return REPORT_HEADINGS_ABBREV;
if (!strcmp(str, "full") || !strcmp(str, "2"))
return REPORT_HEADINGS_FULL;
return REPORT_HEADINGS_UNKNOWN;
}
void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
2007-07-10 22:20:00 +04:00
report_type_t *report_type, const char *separator,
int aligned, int buffered, report_headings_t headings,
int field_prefixes, int quoted, int columns_as_rows,
const char *selection, int multiple_output)
{
uint32_t report_flags = 0;
report: fix lvm devtypes internal error if -S is used with field name from pvs/vgs/lvs Before this fix, when reporting 'lvm devtypes', the report was initialized with incorrect reserved values - the ones used for pvs/vgs/lvs report were used instead of NULL value (because devtypes doesn't have any reserved values). For example, trying to (incorrectly) use lv_name for the -S|--select with lvm devtypes which doesn't have this field at all: Before this patch (internal error issued): $ lvm devtypes -S 'lv_name=lvol0' Internal error: _check_reserved_values_supported: field-specific reserved value of type 0x0 for field not supported Internal error: dm_report_init_with_selection: trying to register unsupported reserved value type, skipping report selection DevType MaxParts Description aoe 16 ATA over Ethernet ataraid 16 ATA Raid bcache 1 bcache block device cache ... With this patch applied (correct error displayed about unrecognized selection field): $ lvm devtypes -S 'lv_name=lvol0' Device Types Fields ------------------- devtype_name - Name of Device Type exactly as it appears in /proc/devices. [string] devtype_max_partitions - Maximum number of partitions. (How many device minor numbers get reserved for each device.) [number] devtype_description - Description of Device Type. [string] Special Fields -------------- selected - Set if item passes selection criteria. [number] help - Show help. [unselectable number] ? - Show help. [unselectable number] Unrecognised selection field: lv_name Selection syntax error at 'lv_name=lvol0'. Use 'help' for selection to get more help.
2016-05-30 17:37:05 +03:00
const struct dm_report_object_type *types;
const struct dm_report_field_type *fields;
const struct dm_report_reserved_value *reserved_values;
void *rh;
if (aligned)
report_flags |= DM_REPORT_OUTPUT_ALIGNED;
if (buffered)
report_flags |= DM_REPORT_OUTPUT_BUFFERED;
if (headings) {
/* any out of bound headings type value maps to REPORT_HEADINGS_ABBREV */
report_flags |= DM_REPORT_OUTPUT_HEADINGS;
if (headings == REPORT_HEADINGS_FULL)
report_flags |= DM_REPORT_OUTPUT_FIELD_IDS_IN_HEADINGS;
}
2008-06-06 23:28:35 +04:00
if (field_prefixes)
report_flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
2008-06-25 01:21:04 +04:00
if (!quoted)
report_flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
2008-06-25 02:48:53 +04:00
if (columns_as_rows)
report_flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
if (multiple_output)
report_flags |= DM_REPORT_OUTPUT_MULTIPLE_TIMES;
if (*report_type & CMDLOG) {
types = _log_report_types;
fields = _log_fields;
reserved_values = NULL;
} else if (*report_type & DEVTYPES) {
report: fix lvm devtypes internal error if -S is used with field name from pvs/vgs/lvs Before this fix, when reporting 'lvm devtypes', the report was initialized with incorrect reserved values - the ones used for pvs/vgs/lvs report were used instead of NULL value (because devtypes doesn't have any reserved values). For example, trying to (incorrectly) use lv_name for the -S|--select with lvm devtypes which doesn't have this field at all: Before this patch (internal error issued): $ lvm devtypes -S 'lv_name=lvol0' Internal error: _check_reserved_values_supported: field-specific reserved value of type 0x0 for field not supported Internal error: dm_report_init_with_selection: trying to register unsupported reserved value type, skipping report selection DevType MaxParts Description aoe 16 ATA over Ethernet ataraid 16 ATA Raid bcache 1 bcache block device cache ... With this patch applied (correct error displayed about unrecognized selection field): $ lvm devtypes -S 'lv_name=lvol0' Device Types Fields ------------------- devtype_name - Name of Device Type exactly as it appears in /proc/devices. [string] devtype_max_partitions - Maximum number of partitions. (How many device minor numbers get reserved for each device.) [number] devtype_description - Description of Device Type. [string] Special Fields -------------- selected - Set if item passes selection criteria. [number] help - Show help. [unselectable number] ? - Show help. [unselectable number] Unrecognised selection field: lv_name Selection syntax error at 'lv_name=lvol0'. Use 'help' for selection to get more help.
2016-05-30 17:37:05 +03:00
types = _devtypes_report_types;
fields = _devtypes_fields;
reserved_values = NULL;
} else {
types = _report_types;
fields = _fields;
reserved_values = _report_reserved_values;
}
rh = dm_report_init_with_selection(report_type, types, fields,
report: select: add support for reserved value recognition in report selection string - add struct dm_report_reserved_value Make dm_report_init_with_selection to accept an argument with an array of reserved values where each element contains a triple: {dm report field type, reserved value, array of strings representing this value} When the selection is parsed, we always check whether a string representation of some reserved value is not hit and if it is, we use the reserved value assigned for this string instead of trying to parse it as a value of certain field type. This makes it possible to define selections like: ... --select lv_major=undefined (or -1 or unknown or undef or whatever string representations are registered for this reserved value in the future) ... --select lv_read_ahead=auto ... --select vg_mda_copies=unmanaged With this, each time the field value of certain type is hit and when we compare it with the selection, we use the proper value for comparison. For now, register these reserved values that are used at the moment (also more descriptive names are used for the values): const uint64_t _reserved_number_undef_64 = UINT64_MAX; const uint64_t _reserved_number_unmanaged_64 = UINT64_MAX - 1; const uint64_t _reserved_size_auto_64 = UINT64_MAX; { {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_undef_64, {"-1", "undefined", "undef", "unknown", NULL}}, {DM_REPORT_FIELD_TYPE_NUMBER, _reserved_number_unmanaged_64, {"unmanaged", NULL}}, {DM_REPORT_FIELD_TYPE_SIZE, _reserved_size_auto_64, {"auto", NULL}}, NULL } Same reserved value of different field types do not collide. All arrays are null-terminated. The list of reserved values is automatically displayed within selection help output: Selection operands ------------------ ... Reserved values --------------- -1, undefined, undef, unknown - Reserved value for undefined numeric value. [number] unmanaged - Reserved value for unmanaged number of metadata copies in VG. [number] auto - Reserved value for size that is automatically calculated. [size] Selection operators ------------------- ...
2014-05-30 17:02:21 +04:00
format, separator, report_flags, keys,
report: fix lvm devtypes internal error if -S is used with field name from pvs/vgs/lvs Before this fix, when reporting 'lvm devtypes', the report was initialized with incorrect reserved values - the ones used for pvs/vgs/lvs report were used instead of NULL value (because devtypes doesn't have any reserved values). For example, trying to (incorrectly) use lv_name for the -S|--select with lvm devtypes which doesn't have this field at all: Before this patch (internal error issued): $ lvm devtypes -S 'lv_name=lvol0' Internal error: _check_reserved_values_supported: field-specific reserved value of type 0x0 for field not supported Internal error: dm_report_init_with_selection: trying to register unsupported reserved value type, skipping report selection DevType MaxParts Description aoe 16 ATA over Ethernet ataraid 16 ATA Raid bcache 1 bcache block device cache ... With this patch applied (correct error displayed about unrecognized selection field): $ lvm devtypes -S 'lv_name=lvol0' Device Types Fields ------------------- devtype_name - Name of Device Type exactly as it appears in /proc/devices. [string] devtype_max_partitions - Maximum number of partitions. (How many device minor numbers get reserved for each device.) [number] devtype_description - Description of Device Type. [string] Special Fields -------------- selected - Set if item passes selection criteria. [number] help - Show help. [unselectable number] ? - Show help. [unselectable number] Unrecognised selection field: lv_name Selection syntax error at 'lv_name=lvol0'. Use 'help' for selection to get more help.
2016-05-30 17:37:05 +03:00
selection, reserved_values, cmd);
if (rh && field_prefixes)
2008-06-06 23:28:35 +04:00
dm_report_set_output_field_name_prefix(rh, "lvm2_");
return rh;
}
void *report_init_for_selection(struct cmd_context *cmd,
report_type_t *report_type,
const char *selection_criteria)
{
return dm_report_init_with_selection(report_type, _report_types, _fields,
"", DEFAULT_REP_SEPARATOR,
DM_REPORT_OUTPUT_FIELD_UNQUOTED,
"", selection_criteria,
_report_reserved_values,
cmd);
}
int report_get_prefix_and_desc(report_type_t report_type_id,
const char **report_prefix,
const char **report_desc)
{
const struct dm_report_object_type *report_types, *report_type;
if (report_type_id & CMDLOG)
report_types = _log_report_types;
else if (report_type_id & DEVTYPES)
report_types = _devtypes_report_types;
else
report_types = _report_types;
for (report_type = report_types; report_type->id; report_type++) {
if (report_type_id & report_type->id) {
*report_prefix = report_type->prefix;
*report_desc = report_type->desc;
return 1;
}
}
*report_prefix = *report_desc = "";
return 0;
}
/*
* Create a row of data for an object
*/
int report_object(void *handle, int selection_only, const struct volume_group *vg,
const struct logical_volume *lv, const struct physical_volume *pv,
const struct lv_segment *seg, const struct pv_segment *pvseg,
const struct lv_with_info_and_seg_status *lvdm,
const struct label *label)
{
struct selection_handle *sh = selection_only ? (struct selection_handle *) handle : NULL;
struct device dummy_device = { .dev = 0 };
struct label dummy_label = { .dev = &dummy_device };
struct lvm_report_object obj = {
.vg = (struct volume_group *) vg,
.lvdm = (struct lv_with_info_and_seg_status *) lvdm,
.pv = (struct physical_volume *) pv,
.seg = (struct lv_segment *) seg,
.pvseg = (struct pv_segment *) pvseg,
.label = (struct label *) (label ? : (pv ? pv_label(pv) : NULL))
};
/* FIXME workaround for pv_label going through cache; remove once struct
* physical_volume gains a proper "label" pointer */
if (!obj.label) {
if (pv) {
if (pv->fmt)
dummy_label.labeller = pv->fmt->labeller;
if (pv->dev)
dummy_label.dev = pv->dev;
else
memcpy(dummy_device.pvid, &pv->id, ID_LEN);
}
obj.label = &dummy_label;
}
/* Never report orphan VGs. */
if (vg && is_orphan_vg(vg->name)) {
obj.vg = &_dummy_vg;
if (pv)
_dummy_fid.fmt = pv->fmt;
}
if (vg && is_orphan_vg(vg->name) && pv && is_used_pv(pv)) {
obj.vg = &_unknown_vg;
_dummy_fid.fmt = pv->fmt;
}
return sh ? dm_report_object_is_selected(sh->selection_rh, &obj, 0, &sh->selected)
: dm_report_object(handle, &obj);
}
static int _report_devtype_single(void *handle, const dev_known_type_t *devtype)
{
return dm_report_object(handle, (void *)devtype);
}
int report_devtypes(void *handle)
{
int devtypeind = 0;
while (_dev_known_types[devtypeind].name[0])
if (!_report_devtype_single(handle, &_dev_known_types[devtypeind++]))
return 0;
return 1;
}
int report_cmdlog(void *handle, const char *type, const char *context,
const char *object_type_name, const char *object_name,
const char *object_id, const char *object_group,
const char *object_group_id, const char *msg,
int current_errno, int ret_code)
{
struct cmd_log_item log_item = {_log_seqnum++, type, context, object_type_name,
object_name ? : "", object_id ? : "",
object_group ? : "", object_group_id ? : "",
msg ? : "", current_errno, ret_code};
if (handle)
return dm_report_object(handle, &log_item);
return 1;
}
void report_reset_cmdlog_seqnum(void)
{
_log_seqnum = 1;
}
int report_current_object_cmdlog(const char *type, const char *msg, int32_t ret_code)
{
log_report_t log_state = log_get_report_state();
return report_cmdlog(log_state.report, type, log_get_report_context_name(log_state.context),
log_get_report_object_type_name(log_state.object_type),
log_state.object_name, log_state.object_id,
log_state.object_group, log_state.object_group_id,
msg, stored_errno(), ret_code);
}