Whamcloud - gitweb
EX-6685 lipe: Add collection of files sizes statistics
authorVitaliy Kuznetsov <vkuznetsov@ddn.com>
Tue, 29 Aug 2023 15:38:59 +0000 (19:38 +0400)
committerAndreas Dilger <adilger@whamcloud.com>
Fri, 1 Sep 2023 13:19:25 +0000 (13:19 +0000)
Add the ability to collect files sizes statistics to lipe_scan3
and lipe_find3.

This improvement adds an additional "collect-fsize-stats" option
to generate a file size statistics report for the conditions
specified in the search request. For example, you can collect
statistics on files for a specific user, on a specific modification
time, size, and other available parameters in files.

collect-fsize-stats is added as a new output policy and excludes
other output options (such as print-file-fid ) when this option
is specified. All calculations are made in KB, as this reduces
the potential volume of output data and makes it easier to work
with them through other programs.

Statistics are printed in 3 formats, .out for easy reading, JSON
and in YAML format. The template formatted output of the reports
themselves was copied from fsstats, but works on the lipe engine.

The report itself consists of the following tables showing
statistics on file sizes:
- Files Size;
- Capacity used;
- Equal overhead;
- Positive overhead;
- Negative overhead;
- Directory size;
- Time since creation;
- Time since last modification;
- Time since last metadata modification;
- Time since last access;
- Filename length;
- Storage size by user;
- Storage size by group;
- Storage size by project ID;
- Stripe count;
- Stripe size;
- Mirror count.

Each of the tables has the following structure:

- Header (Stats name);
- Description of the table for each column (from 0 to 8);
- General values relative to table values and stats type;
- Table with 8 columns.

Also generates time-based reports for each user.
Types:
- Time since creation;
- Time since last modification;
- Time since last metadata modification;
- Time since last access;

Each of the tables has the following structure:
- User info
- Header (Stats name);
- Table with 6 or 7 columns.

The files sizes report file can be generated in 4 output
options (out,yaml,json,csv). To specify the desired type of
report, you need to specify the extension from the available
ones (out, yaml, json, csv) in the file name (For example:
report_name.json). If you want to receive reports of all
types, you must specify the extension ".all" (For example:
report_name.all). In this case, reports of all types
will be generated.

Test-Parameters: trivial testlist=sanity-lipe-scan3
Signed-off-by: Vitaliy Kuznetsov <vkuznetsov@ddn.com>
Change-Id: Ied75497e9a53fe5545a0963560ca0638b4f48c76
Reviewed-on: https://review.whamcloud.com/c/ex/lustre-release/+/50713
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
lipe/man/lipe_find3.1
lipe/src/lipe_find3/lf3_lexer.l
lipe/src/lipe_scan3/Makefile.am
lipe/src/lipe_scan3/lipe-scan3-notes.txt
lipe/src/lipe_scan3/ls3_debug.h
lipe/src/lipe_scan3/ls3_main.c
lipe/src/lipe_scan3/ls3_stats.c [new file with mode: 0644]
lipe/src/lipe_scan3/ls3_stats.h [new file with mode: 0644]
lipe/src/lipe_scan3/tests/lipe.scm
lustre/tests/sanity-lipe-scan3.sh

index 198d50c..6df62ae 100644 (file)
@@ -306,6 +306,20 @@ non empty directory cannot be removed by FID and that even when all
 the files in a directory would be removed in a scan they may not be
 removed by the time the directory is encountered.
 
+.IP "\-collect-fsize-stats \fIreport_name\fR"
+Collect statistics about file size, and create file with report.
+Save detailed statistics to a file, which can be used
+when building visual schemes, and graphs. After specifying the option,
+must specify the full correct path with the new file name that will
+be created after finishing lipe_find3.
+The files sizes report file can be generated in 4 output
+options (out,yaml,json,csv). To specify the desired type of report, you need
+to specify the extension from the available ones (out, yaml, json, csv) in
+the file name (For example: report_name.json).
+If you want to receive reports of all types, you must specify
+the extension ".all" (For example: report_name.all). In this case,
+reports of all types will be generated.
+
 .IP "\-quit"
 Exit immediately. No child processes will be left running, but no more
 paths specified on the command line will be processed.
@@ -401,6 +415,17 @@ size, block count and if avaliable the SoM attributes. For example:
 Print a JSON object describing each file on MDT0042 including all
 available attributes.
 
+.nf
+.B lipe_find3 /dev/../*mdt0000 -collect-fsize-stats my_first_report_file
+File with the results:
+  Path:
+    /home/user/my_first_report_file.out
+    /home/user/my_first_report_file.yaml
+
+.fi
+Collect statistics about file sizes. Save the results to a file:
+\fBmy_first_report_file.out and my_first_report_file.yaml\fP .
+
 .SH KNOWN ISSUES
 The \fB\-size\fP, \fB\-blocks\fP, and timestamp tests give incorrect
 results for striped directories.
index 3b3b9d3..afe8faa 100644 (file)
@@ -75,7 +75,7 @@ static int expr_arg_type;
        BEGIN ARG;
 }
 
--(fprint|fprint0|print-json|printf)\0 {
+-(fprint|fprint0|print-json|printf|collect-fsize-stats)\0 {
        LF3_DEBUG("unary '%s'\n", yytext);
        expr_arg_begin = lf3_arg_index;
        expr_arg_end = lf3_arg_index + 2;
index 340e79e..0f62166 100644 (file)
@@ -12,7 +12,7 @@ bin_PROGRAMS = lipe_scan3
 # Define LUSTRE_UTILS to prevent #error when including lustre_ioctl.h.
 lipe_scan3_CPPFLAGS = -D_GNU_SOURCE -DLUSTRE_UTILS $(GUILE_CFLAGS) -I .. -I ../.. -include config.h
 lipe_scan3_CFLAGS = -Wall -Werror -g -O $(GUILE_CFLAGS) $(ext2fs_CFLAGS) $(json_c_CFLAGS)
-lipe_scan3_LDADD = -llustreapi -llnetconfig $(GUILE_LIBS) $(ext2fs_LIBS) $(json_c_LIBS) -lpthread
+lipe_scan3_LDADD = -llustreapi -llnetconfig $(GUILE_LIBS) $(ext2fs_LIBS) $(json_c_LIBS) -lpthread -lm
 
 lipe_scan3_SOURCES = \
        ../lipe_version.c \
@@ -24,6 +24,8 @@ lipe_scan3_SOURCES = \
        ls3_main.c \
        ls3_object_attrs.c \
        ls3_object_attrs.h \
+       ls3_stats.c \
+       ls3_stats.h \
        ls3_scan.c \
        ls3_scan.h
 
index b84813b..b64e767 100644 (file)
@@ -21,6 +21,7 @@ or more devices:
   lipe_scan3 --print-absolute-path DEVICE...
   lipe_scan3 --print-relative-path DEVICE...
   lipe_scan3 --print-json[=ATTRS] DEVICE...
+  lipe_scan3 --collect-fsize-stats[=FILE_NAME] DEVICE...
 
 In each case the device may be an MDT or an OST. The --print-file-fid
 prints the inode FID when scanning an MDT and the FID of the file
@@ -131,6 +132,7 @@ Policy actions
     (print-json [attrs]) print JSON representation of inode to stdout
     (print-absolute-path) ...
     (print-realtive-path) ...
+    (collect-fsize-stats=[file_name]) create files [file_name] with reports
 
 Break and continue
   These may only be called from a scanning context:
@@ -173,7 +175,7 @@ Example policies:
       (if (not (= (uid)
                   (gid)))
           (print-file-fid)))
-  
+
   Print a line containing the device name and ino:
 
     (lambda ()
index ac593e2..926c687 100644 (file)
@@ -91,6 +91,17 @@ static inline void *xcalloc1(const char *file, int line, const char *func, size_
        return ptr;
 }
 
+static inline void* xrealloc1(const char* file, int line,
+                               const char* func, void* ptr, size_t size)
+{
+       void *new_ptr = realloc(ptr, size);
+
+       if (new_ptr == NULL && size != 0)
+               LS3_OOM_AT(file, line, func, size);
+
+       return new_ptr;
+}
+
 static inline void *xstrdup1(const char *file, int line, const char *func, const char *s)
 {
        void *ptr;
@@ -124,6 +135,7 @@ static inline void *xstrndup1(const char *file, int line, const char *func,
 
 #define xmalloc(size) (xmalloc1(__FILE__, __LINE__, __func__, (size)))
 #define xcalloc(nmemb, size) (xcalloc1(__FILE__, __LINE__, __func__, (nmemb), (size)))
+#define xrealloc(ptr, size) (xrealloc1(__FILE__, __LINE__, __func__, (ptr), (size)))
 #define xstrdup(s) (xstrdup1(__FILE__, __LINE__, __func__, (s)))
 #define xstrndup(s, n) (xstrndup1(__FILE__, __LINE__, __func__, (s), (n)))
 
index 9c07be4..caf9d24 100644 (file)
@@ -28,6 +28,7 @@
 #include "ls3_debug.h"
 #include "ls3_object_attrs.h"
 #include "ls3_scan.h"
+#include "ls3_stats.h"
 
 #define LS3_MODULE_NAME "lipe"
 #define LS3_SCAN "lipe-scan"
@@ -36,6 +37,7 @@
 #define LS3_PRINT_JSON "print-json"
 #define LS3_PRINT_ABSOLUTE_PATH "print-absolute-path"
 #define LS3_PRINT_RELATIVE_PATH "print-relative-path"
+#define LS3_COLLECT_STATS "collect-fsize-stats"
 
 #define LS3_GETOPT_DEVICE_PATH "lipe-getopt-device-path"
 #define LS3_GETOPT_CLIENT_MOUNT_PATH "lipe-getopt-client-mount-path"
@@ -52,6 +54,7 @@ static SCM ls3_scm_fid_vtable = SCM_UNDEFINED;
 SCM ls3_scm_policy_exception_handler;
 
 bool ls3_debug;
+bool save_fsize_stats;
 const char *ls3_progname_;
 __thread int ls3_tid; /* gettid() thread id for debug messages. */
 
@@ -963,7 +966,7 @@ out:
 }
 
 /* We provide some canned printing policies --print-fid, --print-json,
- * --print-absolute-path, --print-relative-path. */
+ * --print-absolute-path, --print-relative-path, --collect-fsize-stats. */
 
 #define LS3_PRINT_DELIM_C(fmt, c, args...) \
        printf(fmt "%c", ##args, c)
@@ -1001,6 +1004,44 @@ SCM_DEFINE(ls3_scm_print_self_fid, LS3_PRINT_SELF_FID, 0, 0, 0,
        return SCM_UNSPECIFIED;
 }
 
+SCM_DEFINE(ls3_scm_collect_fsize_stats, LS3_COLLECT_STATS, 0, 1, 0,
+               (SCM string), "collect file size info of current file")
+{
+       struct ls3_object_attrs *loa_all = ls3_attrs_try(LS3_OBJECT_ATTR_ALL);
+       int rc = 0;
+
+       /* LS3_OBJECT_ATTR_ENTRIES also no work because:
+        * TODO: Make work for striped directories. */
+
+       if (!reports_with_stats->device_path) {
+               struct ls3_instance *li = ls3_instance();
+               reports_with_stats->device_path = xstrdup(li->li_device_path);
+               reports_with_stats->device_name = xstrdup(li->li_device_name);
+               reports_with_stats->client_mount_path =
+                       xstrdup(li->li_client_mount_path);
+               reports_with_stats->device_is_mdt = li->li_device_is_mdt; /* else OST */
+       }
+
+       if (!save_fsize_stats) {
+               save_fsize_stats = true;
+
+               if (!scm_is_null(string)) {
+                       char *c_string;
+                       c_string = scm_to_utf8_string(string);
+                       reports_with_stats->report_file_name = xstrdup(c_string);
+                       free(c_string);
+               }
+       }
+
+       rc = ls3_stats_update_info(loa_all);
+       if (rc) {
+               ls3_stats_destroy();
+               LS3_FATAL("Error: %d updating file size stats\n", rc);
+       }
+
+       return SCM_UNSPECIFIED;
+}
+
 SCM_DEFINE(ls3_scm_print_json, LS3_PRINT_JSON, 0, 2, 0,
           (SCM required_attrs, SCM optional_attrs),
           "print JSON representation of current file")
@@ -1174,34 +1215,37 @@ HELP_WARNING
 "Scan DEVICE... ... and something, something ... scheme  ... to stdout.\n"
 "Exactly one of the following options must be used:\n"
 /* -i, --interactive is intentionally undocumented */
-"  --print-file-fid            print the FID of the file for each object\n"
-"  --print-self-fid            print the FID of the file for each object\n"
-"  --print-json[=ATTRS]        print a JSON object for each file with atributes specified by ATTRS\n"
-"                              ATTRS must be a comma separated list of attributes\n"
-"                              (use '--list-json-attrs' to see available attributes)\n"
-"  --print-relative-path       print a mount relative path for each object\n"
-"  --print-absolute-path       print an absolute path for each object\n"
-"  -s, --script=SCRIPT         support for #! executable scripts\n"
+"  --print-file-fid             print the FID of the file for each object\n"
+"  --print-self-fid             print the FID of the file for each object\n"
+"  --print-json[=ATTRS]         print a JSON object for each file with atributes specified by ATTRS\n"
+"                               ATTRS must be a comma separated list of attributes\n"
+"                               (use '--list-json-attrs' to see available attributes)\n"
+"  --print-relative-path        print a mount relative path for each object\n"
+"  --print-absolute-path        print an absolute path for each object\n"
+"  --collect-fsize-stats[=FILE] save file size statistics in a file [FILE] at\n"
+"                               the end the scan. Available file extensions:\n"
+"                               .out, .yaml, .json, .csv, .all .\n"
+"  -s, --script=SCRIPT          support for #! executable scripts\n"
 "\n"
 "Mandatory arguments to long options are mandatory for short options too.\n"
-"  --all-paths                 print all file paths when files have multiple hardlinks\n"
-"                              for --print-relative-path or --print-absolute-path\n"
-"  --null                      use a zero byte (the ASCII NUL character) to delimit output of --print-*\n"
-"  --client-mount=MOUNT        use the Lustre client at MOUNT for FID to path\n"
-"  --no-client-mount           disable automatic client mount detection\n"
-"  --direct-io=0|1             disable or enable use of direct IO\n"
-"  --io-options=OPTIONS        pass OPTIONS to ext2fs_open2()\n"
-"  --required-attrs=ATTR...    only apply policy to inodes with ATTR...\n"
-"                              (use '--list-attrs' to see available attributes)\n"
-"  --thread-count=COUNT        use COUNT scanning threads\n"
-"                              (default is number of processors / 4)\n"
-"  --list-attrs                list available attribute names and exit\n"
-"  --list-json-attrs           list available JSON attribute names and exit\n"
-"  --debug                     remove insects from device\n"
-"  -l, --load=FILE             read and evaluate FILE before scanning\n"
-"                              multiple --load options may be used\n"
-"  -h, --help                  display this help text and exit\n"
-"  -v, --version               output version information and exit\n"
+"  --all-paths                  print all file paths when files have multiple hardlinks\n"
+"                               for --print-relative-path or --print-absolute-path\n"
+"  --null                       use a zero byte (the ASCII NUL character) to delimit output of --print-*\n"
+"  --client-mount=MOUNT         use the Lustre client at MOUNT for FID to path\n"
+"  --no-client-mount            disable automatic client mount detection\n"
+"  --direct-io=0|1              disable or enable use of direct IO\n"
+"  --io-options=OPTIONS         pass OPTIONS to ext2fs_open2()\n"
+"  --required-attrs=ATTR...     only apply policy to inodes with ATTR...\n"
+"                               (use '--list-attrs' to see available attributes)\n"
+"  --thread-count=COUNT         use COUNT scanning threads\n"
+"                               (default is number of processors / 4)\n"
+"  --list-attrs                 list available attribute names and exit\n"
+"  --list-json-attrs            list available JSON attribute names and exit\n"
+"  --debug                      remove insects from device\n"
+"  -l, --load=FILE              read and evaluate FILE before scanning\n"
+"                               multiple --load options may be used\n"
+"  -h, --help                   display this help text and exit\n"
+"  -v, --version                output version information and exit\n"
 /* TODO --delim=DELIM */
 ,
 program_invocation_short_name, program_invocation_short_name);
@@ -1224,6 +1268,7 @@ enum {
        LS3_OPT_PRINT_RELATIVE_PATH,
        LS3_OPT_REQUIRED_ATTRS,
        LS3_OPT_THREAD_COUNT,
+       LS3_OPT_COLLECT_STATS,
 
        LS3_OPT_HELP = 'h',
        LS3_OPT_INTERACTIVE = 'i',
@@ -1244,6 +1289,7 @@ static struct option options[] = {
        { LS3_PRINT_FILE_FID, no_argument, NULL, LS3_OPT_PRINT_FILE_FID },
        { LS3_PRINT_SELF_FID, no_argument, NULL, LS3_OPT_PRINT_SELF_FID },
        { "print-json", optional_argument, NULL, LS3_OPT_PRINT_JSON },
+       { LS3_COLLECT_STATS, optional_argument, NULL, LS3_OPT_COLLECT_STATS},
        { "print-absolute-path", no_argument, NULL, LS3_OPT_PRINT_ABSOLUTE_PATH },
        { "print-relative-path", no_argument, NULL, LS3_OPT_PRINT_RELATIVE_PATH },
        { "all-paths", no_argument, NULL, LS3_OPT_ALL_PATHS },
@@ -1378,6 +1424,7 @@ static void ls3_module_init(void *unused)
        "print-self-fid",
        "print-absolute-path",
        "print-relative-path",
+       "collect-fsize-stats",
        NULL);
 }
 
@@ -1387,6 +1434,7 @@ static void ls3_main_scm(void *data, int argc, char *argv[])
        const char *load_file;
        const char *script_file = NULL;
        const char *policy = NULL;
+       const char *ls3_report_path = NULL;
        char *end;
        int rc;
        int c;
@@ -1428,6 +1476,12 @@ static void ls3_main_scm(void *data, int argc, char *argv[])
                case LS3_OPT_PRINT_FILE_FID:
                        policy = LS3_PRINT_FILE_FID;
                        break;
+               case LS3_OPT_COLLECT_STATS:
+                       policy = LS3_COLLECT_STATS;
+                       save_fsize_stats = true;
+                       if (optarg)
+                               ls3_report_path = optarg;
+                       break;
                case LS3_OPT_PRINT_SELF_FID:
                        policy = LS3_PRINT_SELF_FID;
                        break;
@@ -1503,9 +1557,14 @@ static void ls3_main_scm(void *data, int argc, char *argv[])
 
        int exit_status = EXIT_SUCCESS;
 
+       ls3_stats_init();
+
        if (policy != NULL) {
                int i;
 
+               if (ls3_report_path)
+                       reports_with_stats->report_file_name = xstrdup(ls3_report_path);
+
                /* Each positional parameter is a device to scan. */
                if (optind == argc)
                        USAGE();
@@ -1540,6 +1599,10 @@ static void ls3_main_scm(void *data, int argc, char *argv[])
                exit_status = ls3_scm_to_exit_status(script_rc);
        }
 
+       if (save_fsize_stats)
+               ls3_stats_printf();
+
+       ls3_stats_destroy();
        LS3_DEBUG_D(exit_status);
        exit(exit_status);
 }
diff --git a/lipe/src/lipe_scan3/ls3_stats.c b/lipe/src/lipe_scan3/ls3_stats.c
new file mode 100644 (file)
index 0000000..bb80c6d
--- /dev/null
@@ -0,0 +1,2106 @@
+/*
+ * Copyright (c) 2023, DDN Storage Corporation.
+ *
+ * Author: Vitaliy Kuznetsov <vkuznetsov@ddn.com>
+ */
+
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <lustre/lustreapi.h>
+#include <json-c/json.h>
+#include "ls3_stats.h"
+
+
+/* Function to calculate the range index of a number.
+ * The range index is based on the power of 2 that is less than
+ * or equal to the number. For instance, the range index of the number 3
+ * will be 1 (since 2^1 <= 3 < 2^2).
+ *
+ * @param num: The input number for which the range index needs to be
+ * calculated. It is assumed that num > 0. If num < 0, the function
+ * will return -1.
+ *
+ * @return The range index of the number.
+ */
+int ls3_stats_get_range_index(uint64_t num)
+{
+       if (num <= 0)
+               return 0;
+
+       return (int)floor(log2(num));
+}
+
+/* Returns the size of the device in bytes if successful; otherwise, 0.
+ *
+ * @device_path: The path of the device.
+ */
+uint64_t ls3_stats_get_dev_size(const char* device_path)
+{
+       int device_fd = open(device_path, O_RDONLY);
+
+       if (device_fd >= 0) {
+               uint64_t dev_size;
+               int res;
+
+               res = ioctl(device_fd, BLKGETSIZE64, &dev_size);
+               if (res >= 0)
+                       return dev_size;
+               else
+                       LS3_ERROR("error obtaining device size: %s\n",
+                                 strerror(errno));
+
+               close(device_fd);
+       } else {
+               LS3_ERROR("error opening device %s for read size: %s\n",
+                         device_path, strerror(errno));
+       }
+
+       return 0;
+}
+
+/* ls3_stats_get_day_diff - Calculates the time difference in
+ * days between a given timestamp and the current real-time.
+ *
+ * @param timestamp: The timestamp to compare
+ * (in seconds since the UNIX epoch).
+ *
+ * @return The difference in days between the given timestamp
+ * and the current real-time.
+ */
+static unsigned int ls3_stats_get_day_diff(time_t timestamp)
+{
+       time_t current_time = time(NULL);
+       unsigned int seconds_per_day = 24 * 60 * 60;
+       unsigned int day_diff = (current_time - timestamp) / seconds_per_day;
+
+       return day_diff;
+}
+
+static const char* ls3_stats_get_username_from_uid(uid_t uid)
+{
+       struct passwd *pw = getpwuid(uid);
+
+       return pw ? pw->pw_name : NULL;
+}
+
+static const char* ls3_stats_get_groupname_from_gid(gid_t gid)
+{
+       struct group *grp = getgrgid(gid);
+
+       return grp ? grp->gr_name : NULL;
+}
+
+static const ls3_stats_extension_mapping ext_mappings[] = {
+       {"out",  LS3_STATS_FILE_EXTENSION_OUT},
+       {"json", LS3_STATS_FILE_EXTENSION_JSON},
+       {"yaml", LS3_STATS_FILE_EXTENSION_YAML},
+       {"csv",  LS3_STATS_FILE_EXTENSION_CSV},
+       {"all",  LS3_STATS_FILE_EXTENSION_ALL}, /* Should always be the last */
+       {NULL, 0}                               /* End marker */
+};
+
+static const char* ls3_stats_get_filename_from_path(const char *path)
+{
+       const char *last_slash = strrchr(path, '/');
+
+       if (last_slash)
+               return last_slash + 1;
+       else
+               return path;
+}
+
+/* ls3_stats_remove_extension_in_file - remove extention in report file name */
+static void ls3_stats_remove_extension_in_file(char *file_path)
+{
+       char *last_dot = strrchr(file_path, '.');
+
+       if (last_dot != NULL)
+               *last_dot = '\0';
+}
+
+static void ls3_stats_change_extension_in_file(int bit_extension)
+{
+       const ls3_stats_extension_mapping *map_ext = ext_mappings;
+
+       for (map_ext = ext_mappings; map_ext->extension_name != NULL; ++map_ext) {
+               if (map_ext->extension_value & bit_extension) {
+                       char buffer[256];
+
+                       snprintf(buffer, sizeof(buffer), "%s.%s",
+                                reports_with_stats->report_file_name,
+                                map_ext->extension_name);
+
+                       if (reports_with_stats->format_report_file_name)
+                               free(reports_with_stats->format_report_file_name);
+
+                       reports_with_stats->format_report_file_name =
+                               xstrdup(buffer);
+                       return;
+               }
+       }
+}
+
+static void ls3_stats_get_report_extension(char *fileName)
+{
+       const ls3_stats_extension_mapping *map_ext = ext_mappings;
+       char *dot = strrchr(fileName, '.');
+       char extension[5]; /* Don't need more 5 symbols */
+       bool marker;
+       int i;
+
+       if(!dot || dot == fileName) {
+               /* By default if filename not have extension */
+               reports_with_stats->report_extension |=
+                       LS3_STATS_FILE_EXTENSION_ALL;
+           return;
+       }
+
+       strncpy(extension, dot+1, sizeof(extension)-1);
+       extension[sizeof(extension)-1] = '\0';
+       for(i = 0; extension[i]; i++)
+               extension[i] = tolower((unsigned char)extension[i]);
+
+       for (map_ext = ext_mappings; map_ext->extension_name != NULL; ++map_ext) {
+               if (strcmp(extension, map_ext->extension_name) == 0) {
+                       reports_with_stats->report_extension |=
+                               map_ext->extension_value;
+                       marker = true;
+                       break;
+               }
+       }
+
+       /* By default if filename have no supported extension */
+       if (!marker)
+               reports_with_stats->report_extension |=
+                       LS3_STATS_FILE_EXTENSION_ALL;
+}
+
+static FILE* ls3_stats_open_file(char *file_name)
+{
+       FILE *file = fopen(file_name, "w");
+       if (file == NULL)
+               LS3_FATAL("error opening file for save stats: %s\n", file_name);
+
+       return file;
+}
+
+/*
+ * ls3_stats_prepare_file accomplishes three main tasks:
+ * 1. Creates the report directory if it doesn't exist already.
+ * 2. Generates a unique file name if the user didn't explicitly specify one.
+ * 3. Determines the user-requested file extension for the report.
+ */
+static void ls3_stats_prepare_file(void)
+{
+       char tmp[256];
+       char *p = NULL;
+       size_t len;
+       int rc = 0;
+
+       snprintf(tmp, sizeof(tmp), "%s", DEFAULT_DIR_FOR_REPORTS);
+       len = strlen(tmp);
+       if (tmp[len - 1] == '/')
+               tmp[len - 1] = '\0';
+
+       for (p = tmp + 1; *p; p++) {
+               if (*p == '/') {
+                       *p = '\0';
+                       rc = mkdir(tmp, S_IRWXU);
+                       if (rc != 0 && errno != EEXIST) {
+                               LS3_ERROR("error creating directory %s %s\n",
+                                         tmp, strerror(errno));
+                               return;
+                       }
+                       *p = '/';
+               }
+       }
+
+       rc = mkdir(tmp, S_IRWXU);
+       if (rc != 0 && errno != EEXIST) {
+               LS3_ERROR("error creating directory %s %s\n",
+                         tmp, strerror(errno));
+               return;
+       }
+
+       /* when using lipe_scan3 --collect-fsize-stats without path to file */
+       if (!reports_with_stats->report_file_name) {
+               char file_name_buffer[256];
+               time_t current_time = time(NULL);
+               struct tm *timeinfo = localtime(&current_time);
+               strftime(file_name_buffer, sizeof(file_name_buffer),
+                        DEFAULT_DIR_FOR_REPORTS
+                        "file_size_report.%Y-%m-%d-%H:%M:%S.out", timeinfo);
+               reports_with_stats->report_file_name = xstrdup(file_name_buffer);
+       }
+
+       ls3_stats_get_report_extension(reports_with_stats->report_file_name);
+       ls3_stats_remove_extension_in_file(reports_with_stats->report_file_name);
+}
+
+static ls3_stats_val_type ls3_stats_get_value_type(
+               ls3_stats_report_type report_type)
+{
+       switch (report_type)
+       {
+               case LS3_STATS_FILES_SIZE:
+               case LS3_STATS_CAPACITY_USED:
+               case LS3_STATS_DIRECTORY_SIZE_KB:
+               case LS3_STATS_EQUAL_OVERHEAD:
+               case LS3_STATS_STORAGE_SET_SIZE_BY_USER:
+               case LS3_STATS_STORAGE_SET_SIZE_BY_GROUP:
+               case LS3_STATS_STORAGE_SET_SIZE_BY_PROJID:
+               case LS3_STATS_STRIPE_SIZE:
+                       return LS3_STATS_VALUE_TYPE_KB;
+               case LS3_STATS_TIME_SINCE_LAST_MOD_RF:
+               case LS3_STATS_TIME_SINCE_LAST_CREATION_RF:
+               case LS3_STATS_TIME_SINCE_LAST_ACCESS_RF:
+               case LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF:
+                       return LS3_STATS_VALUE_TYPE_DAYS;
+               case LS3_STATS_FILENAME_LENGTH:
+                       return LS3_STATS_VALUE_TYPE_CHARS;
+               case LS3_STATS_DIRECTORY_SIZE_ENTRIES:
+                       return LS3_STATS_VALUE_TYPE_ENTRIES;
+               case LS3_STATS_FILES_EMPLOYING_DOM:
+               case LS3_STATS_TOTAL_COUNT_REPORT:
+                       return LS3_STATS_VALUE_TYPE_ERROR;
+               case LS3_STATS_POSITIVE_OVERHEAD:
+               case LS3_STATS_NEGATIVE_OVERHEAD:
+                       return LS3_STATS_VALUE_TYPE_BYTES;
+               case LS3_STATS_LINK_COUNT:
+                       return LS3_STATS_VALUE_TYPE_LINKS;
+               case LS3_STATS_STRIPE_COUNT:
+                       return LS3_STATS_VALUE_TYPE_STRIPE;
+               case LS3_STATS_MIRROR_COUNT:
+                       return LS3_STATS_VALUE_TYPE_MIRROR;
+       }
+
+       return LS3_STATS_VALUE_TYPE_ERROR;
+}
+
+
+static char *ls3_stats_get_str_value_type(ls3_stats_val_type num_type)
+{
+       switch (num_type)
+       {
+               case LS3_STATS_VALUE_TYPE_ERROR:
+               case LS3_STATS_VALUE_TYPE_EMPTY:
+                       return "";
+               case LS3_STATS_VALUE_TYPE_KB:
+                       return "KB";
+               case LS3_STATS_VALUE_TYPE_ENTRIES:
+                       return "Entries";
+               case LS3_STATS_VALUE_TYPE_CHARS:
+                       return "Chars";
+               case LS3_STATS_VALUE_TYPE_LINKS:
+                       return "Links";
+               case LS3_STATS_VALUE_TYPE_DAYS:
+                       return "Days";
+               case LS3_STATS_VALUE_TYPE_BYTES:
+                       return "Bytes";
+               case LS3_STATS_VALUE_TYPE_STRIPE:
+                       return "Stripe";
+               case LS3_STATS_VALUE_TYPE_MIRROR:
+                       return "Mirror";
+       }
+
+       return "";
+}
+
+static char *ls3_stats_get_title_text(ls3_stats_report_type report_type)
+{
+       switch (report_type)
+       {
+               case LS3_STATS_FILES_SIZE:
+                       return "Files Size";
+               case LS3_STATS_CAPACITY_USED:
+                       return "Capacity used (Regualr file's space used on disk)";
+               case LS3_STATS_EQUAL_OVERHEAD:
+                       return "Equal overhead (Files whose capacity used is"
+                               " equal to their size)";
+               case LS3_STATS_POSITIVE_OVERHEAD:
+                       return "Positive overhead (Files whose capacity used is"
+                               " larger than or equal to its size)";
+               case LS3_STATS_NEGATIVE_OVERHEAD:
+                       return "Negative overhead (Files whose size is larger"
+                               " than capacity used.)";
+               case LS3_STATS_DIRECTORY_SIZE_KB:
+                       return "Directory size (KB's)";
+               case LS3_STATS_TIME_SINCE_LAST_CREATION_RF:
+                       return "Time since creation [crtime](Regular files)";
+               case LS3_STATS_TIME_SINCE_LAST_MOD_RF:
+                       return "Time since last modification [mtime](Regular files)";
+               case LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF:
+                       return "Time since last metadata modification [ctime]";
+               case LS3_STATS_TIME_SINCE_LAST_ACCESS_RF:
+                       return "Time since last access [atime](Regular files)";
+               case LS3_STATS_FILENAME_LENGTH:
+                       return "Filename length (Includes regular files, "
+                               "dirs, symlinks and special files)";
+               case LS3_STATS_DIRECTORY_SIZE_ENTRIES:
+                       return "Directory size (entries)";
+               case LS3_STATS_FILES_EMPLOYING_DOM:
+                       return "Files employing DoM";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_USER:
+                       return "Storage size (all directories and files) by user";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_GROUP:
+                       return "Storage size (all directories and files) by group";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_PROJID:
+                       return "Storage size (all directories and files)"
+                               " by project ID";
+               case LS3_STATS_LINK_COUNT:
+                       return "Link count (of regular files)";
+               case LS3_STATS_STRIPE_COUNT:
+                       return "Stripe count (of regular files)";
+               case LS3_STATS_STRIPE_SIZE:
+                       return "Stripe size (of regular files)";
+               case LS3_STATS_MIRROR_COUNT:
+                       return "Mirror count (of regular files)";
+               case LS3_STATS_TOTAL_COUNT_REPORT:
+                       return "";
+       }
+
+       return "";
+}
+
+static char *ls3_stats_get_header_text(ls3_stats_report_type report_type)
+{
+       switch (report_type)
+       {
+               case LS3_STATS_FILES_SIZE:
+                       return "\n\nFiles Size:\n"
+                               "0 - Size range;\n1 - Count of files in range;\n2 -"
+                               " Number of files in range as a percent of total"
+                               " number of files;\n3 - Number of files in this"
+                               " range or smaller as a % of total # of files;\n"
+                               "4 - Total size of files in range;\n5 - Total size of"
+                               " files in range as a % of total size of files;\n"
+                               "6 - Total size of files in this range or smaller"
+                               " as a % of total size of files;\n7 - Minimum value "
+                               "in range;\n8 - Maximum value in range.\n";
+               case LS3_STATS_CAPACITY_USED:
+                       return "\n\nCapacity used (Regualr file's space used on disk):\n"
+                               "0 - Size range;\n1 - Count of files in range;\n2 - Number of"
+                               " files in range as a percent of total number of"
+                               " files;\n3 - Number of files in this range or smaller"
+                               " as a % of total # of file;\n4 - Total capacity used"
+                               " in range;\n5 - Total capacity used in range as"
+                               " a % of total capacity used;\n6 - Total capacity"
+                               " used in this range or smaller as a % of"
+                               " total capacity;\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_EQUAL_OVERHEAD:
+                       return "\n\nEqual overhead (Files whose usable capacity is"
+                               " equal to their size):\n0 - Size range;\n"
+                               "1 - Count of files in range;\n2 - Number"
+                               " of files in range as a percent of total number of"
+                               " files;\n3 - Number of files in this range or"
+                               " smaller as a % of total # of files;\n4 - Total"
+                               " kilobytes that overflowed in range;\n5 - Total kilobytes"
+                               " that overflowed in this range as a % of total"
+                               " kilobytes that overflowed;\n6 - Total kilobytes that"
+                               " overflowed in this range or smaller as a % of"
+                               " total kilobytes that overflowed;\n7 - Minimum value "
+                               "in range;\n8 - Maximum value in range.\n";
+               case LS3_STATS_POSITIVE_OVERHEAD:
+                       return "\n\nPositive overhead (Files whose capacity used is"
+                               " larger than to its size):\n0 - Range of difference"
+                               " sizes;\n1 - Count of files in range;\n2 - Number"
+                               " of files in range as a percent of total number of"
+                               " files;\n3 - Number of files in this range or"
+                               " smaller as a % of total # of files;\n4 - Total"
+                               " bytes that overflowed in range;\n5 - Total bytes"
+                               " that overflowed in this range as a % of total"
+                               " bytes that overflowed;\n6 - Total bytes that"
+                               " overflowed in this range or smaller as a % of"
+                               " total bytes that overflowed;\n7 - Minimum value "
+                               "in range;\n8 - Maximum value in range.\n";
+               case LS3_STATS_NEGATIVE_OVERHEAD:
+                       return "\n\nNegative overhead (Files whose size is larger"
+                               " than capacity used.):\n0 - Range of difference"
+                               " sizes;\n1 - Count of files in range;\n2 - Number"
+                               " of files in range as a percent of total number of"
+                               " files;\n3 - Number of files in this range or"
+                               " smaller as a % of total # of files;\n4 - Total"
+                               " bytes that underflowed in range;\n5 - Total bytes"
+                               " that underflowed in this range as a % of total"
+                               " bytes that underflowed;\n6 - Total bytes that"
+                               " underflowed in this range or smaller as a % of"
+                               " total bytes that underflowed;\n7 - Minimum value "
+                               "in range;\n8 - Maximum value in range.\n";
+               case LS3_STATS_DIRECTORY_SIZE_KB:
+                       return "\n\nDirectory size (KB's):\n0 - Range of dir sizes;"
+                               " \n1 - Count of directories in range;\n2 - Number"
+                               " of files in dirs as a percent of total number of"
+                               " dirs;\n3 - Number of dirs in this range or"
+                               " smaller as a % of total # of dirs;\n4 - Total size of"
+                               " dirs in range;\n5 - Total size of dirs in range as a"
+                               " % of total size of dirs;\n6 - Total size of dirs in"
+                               " this range or smaller as a % of total size of dirs;"
+                               "\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_TIME_SINCE_LAST_CREATION_RF:
+                       return "\n\nTime since creation [crtime](Regular files):\n"
+                               "0 - Range in days;\n1 - Count of files in range;\n"
+                               "2 - Number of files in range as a % of total"
+                               " number of files;\n3 - Number of files in this"
+                               " range or smaller as a % of total number of"
+                               " files;\n4 - Total kilobytes that underflowed in range;\n"
+                               "5 - Total of age in range, total age in range as"
+                               " a % of total age of all files;\n6 - total age in"
+                               " this range or smaller as a % of total age of"
+                               " all files;\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_TIME_SINCE_LAST_MOD_RF:
+                       return "\n\nTime since last modification [mtime](Regular files):\n"
+                               "0 - Range in days;\n1 - Count of files in range;\n"
+                               "2 - Number of files in range as a % of total"
+                               " number of files;\n3 - Number of files in this"
+                               " range or smaller as a % of total number of"
+                               " files;\n4 - Total kilobytes that underflowed in range;\n"
+                               "5 - Total of age in range, total age in range as"
+                               " a % of total age of all files;\n6 - total age in"
+                               " this range or smaller as a % of total age of"
+                               " all files;\n7 - Minimum value in range;\n8 -"
+                               " Maximum value in range.\n";
+               case LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF:
+                       return "\n\nTime since last metadata modification [ctime]"
+                               " (Regular files):\n0 - Range in days;\n1 - Count"
+                               " of files in range;\n2 - Number of files in range"
+                               " as a % of total number of files;\n3 - Number of"
+                               " files in this range or smaller as a % of total number of"
+                               " files;\n4 - Total kilobytes that underflowed in range;\n"
+                               "5 - Total of age in range, total age in range as"
+                               " a % of total age of all files;\n6 - total age in"
+                               " this range or smaller as a % of total age of"
+                               " all files;\n7 - Minimum value in range;\n8 -"
+                               " Maximum value in range.\n";
+               case LS3_STATS_TIME_SINCE_LAST_ACCESS_RF:
+                       return "\n\nTime since last access [atime](Regular files):\n"
+                               "0 - Range in days;\n1 - Count of files in range;\n"
+                               "2 - Number of files in range as a % of total"
+                               " number of files;\n3 - Number of files in this"
+                               " range or smaller as a % of total number of"
+                               " files;\n4 - Total kilobytes that underflowed in range;\n"
+                               "5 - Total of age in range, total age in range as"
+                               " a % of total age of all files;\n6 - total age in"
+                               " this range or smaller as a % of total age of"
+                               " all files;\n7 - Minimum value in range;\n8 -"
+                               " Maximum value in range.\n";
+               case LS3_STATS_FILENAME_LENGTH:
+                       return "\n\nFilename length (Includes regular files,"
+                               " dirs, symlinks and special files):\n0 - Range"
+                               " of filename lengths;\n1 - Count of files in range;\n"
+                               "2 - Count of files in range as a % of total number"
+                               " of files;\n3 - Number of files in this range or"
+                               " smaller as a % of total # of files;\n"
+                               "4 - Total chars of filenames in range;\n"
+                               "5 - Total chars of filenames in range as a % of"
+                               " total number of chars of filenames;\n6 - Total"
+                               " number of chars of filenames in this range or"
+                               " smaller as a % of total number bytes of filenames;\n"
+                               "7 - Minimum value in range;\n8 -"
+                               " Maximum value in range.\n";
+               case LS3_STATS_DIRECTORY_SIZE_ENTRIES:
+                       return "\n\nDirectory size (entries):\n0 - Range of entries;\n"
+                               "1 - Count of directories in range;\n2 - Number of dirs in"
+                               " range as a % of total num of dirs;\n3 - Number of dirs"
+                               " in this range or smaller as a % total number of dirs;\n"
+                               "4 - Total entries in range;\n5 - Number of entries in"
+                               " range as a % of total number of entries;\n6 - Number of"
+                               " entries in this range or smaller as a % of total number"
+                               " of entries;\n7 - Minimum value in range;\n8 -"
+                               " Maximum value in range.\n";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_USER:
+                       return "\n\nStorage size by user (all directories and"
+                               " files):\n0 - User ID (name);\n1 - Count of files for user"
+                               ";\n2 - Number of files for user as a percent of total"
+                               " number of files;\n3 - Number of files for user as a % of"
+                               " total # of files;\n4 - Total size of files for user;\n5"
+                               " - Total size of files for user as a % of total size of"
+                               " files;\n6 - Total size of files for user or smaller"
+                               " as a % of total size of files;\n7 - Minimum size "
+                               "for user;\n8 - Maximum size for user.\n";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_GROUP:
+                       return "\n\nStorage size by group (all directories and"
+                               " files):\n0 - Group ID (name);\n1 - Count of files for group"
+                               ";\n2 - Number of files for group as a percent of total"
+                               " number of files;\n3 - Number of files for group as a % of"
+                               " total # of files;\n4 - Total size of files for group;\n5"
+                               " - Total size of files for group as a % of total size of"
+                               " files;\n6 - Total size of files for group or smaller"
+                               " as a % of total size of files;\n7 - Minimum size "
+                               "for group;\n8 - Maximum size for group.\n";
+               case LS3_STATS_STORAGE_SET_SIZE_BY_PROJID:
+                       return "\n\nStorage size by projid (all directories and"
+                               " files):\n0 - Project ID;\n1 - Count of files for projid"
+                               ";\n2 - Number of files for projid as a percent of total"
+                               " number of files;\n3 - Number of files for projid as a % of"
+                               " total # of files;\n4 - Total size of files for projid;\n5"
+                               " - Total size of files for projid as a % of total size of"
+                               " files;\n6 - Total size of files for projid or smaller"
+                               " as a % of total size of files;\n7 - Minimum size "
+                               "for projid;\n8 - Maximum size for projid.\n";
+               case LS3_STATS_LINK_COUNT:
+                       return "\n\nLink count (of regular files):\n0 - Range of link"
+                               " counts;\n1 - Count of files in range;\n2 - Number of files"
+                               " in range as a % of total files;\n3 - Number of files in"
+                               " this range or smaller as a % of total files;\n4 - Total"
+                               " links in range;\n5 - Total links in range as a % of total"
+                               " links;\n6 - Total links in this range or smaller as a % of"
+                               " total links;\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_STRIPE_COUNT:
+                       return "\n\nStripe count (of regular files):\n0 - Range of stripe"
+                               " count;\n1 - Count of files in range;\n2 - Number of files"
+                               " in range as a % of total files;\n3 - Number of files in"
+                               " this range as a % of total files;\n4 - Total"
+                               " stripe in range;\n5 - Total stripe in range as a % of total"
+                               " stripe;\n6 - Total stripe in this range as a"
+                               " % of total stripe;\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_STRIPE_SIZE:
+                       return "\n\nStripe size (of regular files):\n0 - Stripe"
+                               " size;\n1 - Count of files with stripe size;\n2 - Number"
+                               " of files with stripe size as a % of total files;\n3"
+                               " - Number of files with this stripe size as a % of"
+                               " total files;\n4 - Total file size with this stripe"
+                               " size;\n5 - Total file size with stripe size as a % of"
+                               " total size;\n6 - Total file size with this stripe size or"
+                               " smaller as a % of total file;\n7 - Minimum value with this"
+                               " stripe size;\n8 - Maximum value with this stripe size.\n";
+               case LS3_STATS_MIRROR_COUNT:
+                       return "\n\nMirror count (of regular files):\n0 - Range of mirror"
+                               " count;\n1 - Count of files in range;\n2 - Number of files"
+                               " in range as a % of total files;\n3 - Number of files in"
+                               " this range as a % of total files;\n4 - Total"
+                               " mirror in range;\n5 - Total mirror in range as a % of total"
+                               " mirror;\n6 - Total mirror in this range as a"
+                               " % of total mirror;\n7 - Minimum value in range;\n"
+                               "8 - Maximum value in range.\n";
+               case LS3_STATS_FILES_EMPLOYING_DOM:
+               case LS3_STATS_TOTAL_COUNT_REPORT:
+                       return "";
+       }
+       return "";
+}
+
+static void ls3_stats_get_range_str(struct range_report_template *range_ptr,
+       char str[30], int range, ls3_stats_report_type r_type, char *value_ts)
+{
+       int size_str = 30;
+
+       /* Different type of "range" field */
+       switch (r_type)
+       {
+               case LS3_STATS_STORAGE_SET_SIZE_BY_USER:
+                       snprintf(str, size_str,
+                                "%10lu (%14s)", range_ptr->rrt_id,
+                                ls3_stats_get_username_from_uid(range_ptr->rrt_id));
+                       break;
+               case LS3_STATS_STORAGE_SET_SIZE_BY_GROUP:
+                       snprintf(str, size_str,
+                                "%10lu (%14s)", range_ptr->rrt_id,
+                                ls3_stats_get_groupname_from_gid(range_ptr->rrt_id));
+                       break;
+               case LS3_STATS_STORAGE_SET_SIZE_BY_PROJID:
+                       snprintf(str, size_str, "%26lu ", range_ptr->rrt_id);
+                       break;
+               case LS3_STATS_STRIPE_SIZE:
+                       snprintf(str, size_str, "%18lu %7s ", range_ptr->rrt_id,
+                                value_ts);
+                       break;
+               case LS3_STATS_MIRROR_COUNT:
+                       snprintf(str, size_str, "%26lu ", range_ptr->rrt_id);
+                       break;
+               case LS3_STATS_TIME_SINCE_LAST_ACCESS_RF:
+               case LS3_STATS_TIME_SINCE_LAST_CREATION_RF:
+               case LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF:
+               case LS3_STATS_TIME_SINCE_LAST_MOD_RF:
+                       if (range_ptr->range_start >= 16384)
+                               snprintf(str, size_str,
+                                        "       Never changed       ");
+                       else
+                               snprintf(str, size_str,
+                                        "%8lu - %8lu %7s",
+                                        range_ptr->range_start,
+                                        range_ptr->range_end, value_ts);
+                       break;
+               default:
+                       snprintf(str, size_str,
+                                "%8lu - %8lu %7s", range_ptr->range_start,
+                                range_ptr->range_end, value_ts);
+                       break;
+       }
+}
+
+/*
+ * json_add_field_with_value is used to add a new field to a given JSON object.
+ * It takes a field name and a value (along with its type) or percentage, and
+ * formats them into a string.
+ * This string is then added as a value for the new field in the JSON object.
+ */
+static void json_add_field_with_value(json_object* jobj,
+       const char* field_name, uint64_t val1, const char* val_type,
+       double percent)
+{
+       char buffer[64] = {0};  /* Always not more 64 symbols */
+
+       if (val_type && val_type[0] != '\0')
+               sprintf(buffer, "%lu %s", val1, val_type);
+       else
+               sprintf(buffer, "%.2f %%", percent);
+
+       json_object_object_add(jobj, field_name,
+                               json_object_new_string(buffer));
+}
+
+/* ls3_stats_get_time_str - сonvert a given number of seconds (since the epoch)
+ * to a human-readable date-time string format.
+ *
+ * @param buffer Output buffer to store the resulting date-time string.
+ * @param seconds Number of seconds since the epoch.
+ */
+static void ls3_stats_get_time_str(char *buffer, uint64_t seconds)
+{
+       time_t l_access;
+       struct tm* la_time;
+       int buffer_size = 20;
+
+       l_access = (time_t)seconds;
+       la_time = localtime(&l_access);
+       if (seconds == 0)
+               strncpy(buffer, "Files never opened", buffer_size);
+       else
+               strftime(buffer, buffer_size, "%Y-%m-%d-%H:%M:%S", la_time);
+}
+
+static char* ls3_stats_get_title_for_user_report(ls3_stats_users_reports rtype)
+{
+       switch (rtype)
+       {
+               case LS3_STATS_USERS_REPORTS_MOD_RF:
+                       return "Time since last modification (mtime)";
+               case LS3_STATS_USERS_REPORTS_MD_MOD_RF:
+                       return "Time since last metadata modification (ctime)";
+               case LS3_STATS_USERS_REPORTS_CREATION_RF:
+                       return "Time since creation (crtime)";
+               case LS3_STATS_USERS_REPORTS_ACCESS_RF:
+                       return "Time since last access (atime)";
+               case LS3_STATS_USERS_REPORTS_COUNTER:
+               default:
+                       return "";
+       }
+}
+
+static void ls3_stats_print_to_json(const char *f_time, double e_time)
+{
+       FILE *json_fd;
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       struct ls3_stats_user_report_template *u_report_ptr;
+       struct json_object *jobj_main = json_object_new_object();
+       struct json_object *jobj_reports = json_object_new_array();
+       struct json_object *jobj_users_reports = json_object_new_array();
+       const char *json_str;
+       char *title_text;
+       char *value_ts;
+       char *second_value_ts;
+       int i, j, k;
+
+       json_object_object_add(jobj_main, "LIPEStatsVersion",
+                              json_object_new_double(LS3_STATS_VERSION));
+       json_object_object_add(jobj_main, "CreateTime",
+                              json_object_new_string(f_time));
+       json_object_object_add(jobj_main, "ProcessedFiles",
+                              json_object_new_int64(reports_with_stats->total_count_files));
+       json_object_object_add(jobj_main, "ProcessingTimeSecs",
+                              json_object_new_double(e_time));
+       json_object_object_add(jobj_main, "DevicePath",
+                              json_object_new_string(reports_with_stats->device_path));
+       json_object_object_add(jobj_main, "DeviceName",
+                              json_object_new_string(reports_with_stats->device_name));
+       json_object_object_add(jobj_main, "DeviceType",
+                              json_object_new_string(reports_with_stats->device_is_mdt ?
+                                                     "MDT" : "OST"));
+       json_object_object_add(jobj_main, "DeviceSizeInKB",
+                              json_object_new_int64(reports_with_stats->device_size >> 10));
+       json_object_object_add(jobj_main, "ClientMountPath",
+                              json_object_new_string(reports_with_stats->client_mount_path));
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               struct json_object *jobj_report;
+               struct json_object *jobj_general;
+               struct json_object *jobj_ranges;
+
+               report_ptr = reports_with_stats->reports[i];
+               if (report_ptr == NULL)
+                       LS3_FATAL("Unable to access the report data: %d\n", i);
+
+               if (report_ptr->files_count == 0)
+                       continue; /* Nothing for print */
+
+               value_ts = ls3_stats_get_str_value_type(report_ptr->value_type);
+               second_value_ts = ls3_stats_get_str_value_type(report_ptr->second_value_type);
+               title_text = ls3_stats_get_title_text(i);
+
+               jobj_report = json_object_new_object();
+               jobj_general = json_object_new_object();
+               jobj_ranges = json_object_new_array();
+
+               json_object_object_add(jobj_general, "Title",
+                                              json_object_new_string(title_text));
+               json_object_object_add(jobj_general, "Count",
+                       json_object_new_int64(report_ptr->files_count));
+               json_add_field_with_value(jobj_general, "Min",
+                       report_ptr->min, value_ts, LS3_STATS_EMPTY_VALUE);
+               json_add_field_with_value(jobj_general, "Max",
+                       report_ptr->max, value_ts, LS3_STATS_EMPTY_VALUE);
+               json_add_field_with_value(jobj_general, "Avg",
+                       report_ptr->avg, value_ts, LS3_STATS_EMPTY_VALUE);
+               json_add_field_with_value(jobj_general, "Total",
+                       report_ptr->total_value,
+                       (second_value_ts[0] != '\0') ?
+                        second_value_ts : value_ts, LS3_STATS_EMPTY_VALUE);
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       struct json_object *jobj_range = json_object_new_object();
+                       char range_t[30];
+
+                       range_ptr = report_ptr->fs_ranges[j];
+                       if (range_ptr == NULL)
+                               continue;
+
+                       if (range_ptr->count_in_range == 0)
+                               continue;       /* Nothing for print */
+
+                       ls3_stats_get_range_str(range_ptr, range_t, j, i, value_ts);
+
+                       json_object_object_add(jobj_range, "RangeID",
+                                              json_object_new_int(j));
+
+                       json_object_object_add(jobj_range, "RangeStr",
+                                              json_object_new_string(range_t));
+
+                       json_object_object_add(jobj_range, "CountInRange",
+                                              json_object_new_int64(
+                                              range_ptr->count_in_range));
+
+                       json_add_field_with_value(jobj_range, "PercentageInRange",
+                                                 LS3_STATS_EMPTY_VALUE ,
+                                                 "", range_ptr->percentage);
+
+                       json_add_field_with_value(jobj_range,
+                                                 "CumulativePercentageInRange",
+                                                 LS3_STATS_EMPTY_VALUE , "",
+                                                 range_ptr->cumulative_percentage);
+
+                       json_add_field_with_value(jobj_range, "TotalInRange",
+                                                 range_ptr->total_in_range,
+                                                 (second_value_ts[0] != '\0') ?
+                                                 second_value_ts : value_ts,
+                                                 LS3_STATS_EMPTY_VALUE);
+
+                       json_add_field_with_value(jobj_range, "PercentTotalInRange",
+                                                 LS3_STATS_EMPTY_VALUE ,
+                                                 "", range_ptr->percent_in_range);
+
+                       json_add_field_with_value(jobj_range,
+                                                 "PercentCumulativeTotalInRange",
+                                                 LS3_STATS_EMPTY_VALUE, "",
+                                                 range_ptr->percent_cumulative_in_range);
+
+                       json_add_field_with_value(jobj_range, "MinValueInRange",
+                                                 range_ptr->min, value_ts,
+                                                 LS3_STATS_EMPTY_VALUE);
+
+                       json_add_field_with_value(jobj_range, "MaxValueInRange",
+                                                 range_ptr->max, value_ts,
+                                                 LS3_STATS_EMPTY_VALUE);
+
+                       json_object_array_add(jobj_ranges, jobj_range);
+               }
+
+               json_object_object_add(jobj_report, "GeneralInfo", jobj_general);
+               json_object_object_add(jobj_report, "Ranges", jobj_ranges);
+               json_object_array_add(jobj_reports, jobj_report);
+       }
+       json_object_object_add(jobj_main, "Reports", jobj_reports);
+
+       /* Printf user time reports */
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               struct json_object *jobj_user_report;
+               struct json_object *jobj_tables;
+               const char *user_name;
+
+               u_report_ptr = reports_with_stats->users_reports[i];
+               if (!u_report_ptr)
+                       continue;
+
+               jobj_user_report = json_object_new_object();
+               jobj_tables = json_object_new_array();
+               user_name = ls3_stats_get_username_from_uid(u_report_ptr->uid);
+
+               json_object_object_add(jobj_user_report, "UserName",
+                                      json_object_new_string(user_name));
+               json_object_object_add(jobj_user_report, "UserUID",
+                                      json_object_new_int64(u_report_ptr->uid));
+
+               for (j = 0; j < LS3_STATS_USERS_REPORTS_COUNTER; j++) {
+                       struct json_object *jobj_table = json_object_new_object();
+                       struct json_object *jobj_ranges = json_object_new_array();
+
+                       title_text = ls3_stats_get_title_for_user_report(j);
+                       report_ptr = u_report_ptr->user_reports[j];
+                       if (report_ptr == NULL)
+                               LS3_FATAL("Unable to access the user report data: %d\n", j);
+
+                       value_ts =
+                               ls3_stats_get_str_value_type(report_ptr->value_type);
+                       second_value_ts =
+                               ls3_stats_get_str_value_type(report_ptr->second_value_type);
+
+                       json_object_object_add(jobj_table, "Title",
+                                              json_object_new_string(title_text));
+
+                       for (k = 0; k < report_ptr->count_ranges; k++) {
+                               struct json_object *jobj_range = json_object_new_object();
+                               char buffer[20];
+
+                               range_ptr = report_ptr->fs_ranges[k];
+                               if (range_ptr == NULL)
+                                       continue;
+
+                               ls3_stats_get_time_str(buffer, range_ptr->last_time_access);
+                               json_object_object_add(jobj_range,
+                                                      "RangeDayStart",
+                                                      json_object_new_int64(range_ptr->range_start));
+                               json_object_object_add(jobj_range,
+                                                      "RangeDayEnd",
+                                                      json_object_new_int64(range_ptr->range_end));
+                               json_object_object_add(jobj_range,
+                                                      "CountFilesInRange",
+                                                      json_object_new_int64(range_ptr->count_in_range));
+                               json_object_object_add(jobj_range,
+                                                      "MinSizeKB",
+                                                      json_object_new_int64(range_ptr->min));
+                               json_object_object_add(jobj_range,
+                                                      "MaxSizeKB",
+                                                      json_object_new_int64(range_ptr->min));
+                               json_object_object_add(jobj_range,
+                                                      "TotalSizeInRangeKB",
+                                                      json_object_new_int64(range_ptr->total_in_range));
+                               json_object_object_add(jobj_range,
+                                                      "LastTimeAccess",
+                                                      json_object_new_string(buffer));
+
+                               json_object_array_add(jobj_ranges, jobj_range);
+                       }
+                       json_object_object_add(jobj_table, "Ranges", jobj_ranges);
+                       json_object_array_add(jobj_tables, jobj_table);
+               }
+               json_object_object_add(jobj_user_report, "Tables", jobj_tables);
+               json_object_array_add(jobj_users_reports, jobj_user_report);
+       }
+
+       json_object_object_add(jobj_main, "UserTimeReports", jobj_users_reports);
+       json_str = json_object_to_json_string_ext(jobj_main,
+                                                 JSON_C_TO_STRING_PRETTY);
+
+       ls3_stats_change_extension_in_file(LS3_STATS_FILE_EXTENSION_JSON);
+
+       json_fd =
+               ls3_stats_open_file(reports_with_stats->format_report_file_name);
+
+       fprintf(json_fd, "%s", json_str);
+       json_object_put(jobj_main);
+       fclose(json_fd);
+       printf("  Path to json: %s\n", reports_with_stats->format_report_file_name);
+}
+
+static void ls3_stats_print_to_yaml(const char *f_time, double e_time)
+{
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       char *second_value_ts;
+       char *value_ts;
+       FILE *yaml_fd;
+       int i, j, k;
+
+       ls3_stats_change_extension_in_file(LS3_STATS_FILE_EXTENSION_YAML);
+       yaml_fd = ls3_stats_open_file(reports_with_stats->format_report_file_name);
+
+       fprintf(yaml_fd,
+               "GeneratedBy: LIPE stats v%.2f\n"
+               "CreateReportTime: %s\n"
+               "ProcessedFiles: %lu\n"
+               "ProcessingTimeSecs: %.0f\n"
+               "DevicePath: %s\n"
+               "DeviceName: %s\n"
+               "ClientMountPath: %s\n"
+               "DeviceSizeInKB: %lu\n"
+               "DeviceType: %s\n",
+               LS3_STATS_VERSION, f_time,
+               reports_with_stats->total_count_files, e_time,
+               reports_with_stats->device_path,
+               reports_with_stats->device_name,
+               reports_with_stats->client_mount_path,
+               reports_with_stats->device_size >> 10,
+               reports_with_stats->device_is_mdt ? "MDT" : "OST");
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               char *title_text;
+
+               report_ptr = reports_with_stats->reports[i];
+               if (report_ptr == NULL)
+                       LS3_FATAL("Unable to access the report data: %d\n", i);
+
+               if (report_ptr->files_count == 0)
+                       continue; /* Nothing for print */
+
+               value_ts = ls3_stats_get_str_value_type(report_ptr->value_type);
+               second_value_ts =
+                       ls3_stats_get_str_value_type(report_ptr->second_value_type);
+               title_text = ls3_stats_get_title_text(i);
+
+               fprintf(yaml_fd,
+                       "\n%s:\n"
+                       "  General:\n"
+                       "    Count: %lu\n"
+                       "    Min: %lu %s\n"
+                       "    Max: %lu %s\n"
+                       "    Avg: %lu %s\n"
+                       "    Total: %lu %s\n"
+                       "  Ranges:\n",
+                       title_text, report_ptr->files_count,
+                       report_ptr->min, value_ts,
+                       report_ptr->max, value_ts,
+                       report_ptr->avg, value_ts,
+                       report_ptr->total_value,
+                       (second_value_ts[0] != '\0') ?
+                       second_value_ts : value_ts);
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       char range_t[30];
+
+                       range_ptr = report_ptr->fs_ranges[j];
+                       if (range_ptr == NULL)
+                               continue;
+
+                       if (range_ptr->count_in_range == 0)
+                               continue;       /* Nothing for print */
+
+                       /* Different type of "range" field */
+                       ls3_stats_get_range_str(range_ptr, range_t, j, i, value_ts);
+
+                       fprintf(yaml_fd,
+                               "    - RangeID: %i\n"
+                               "      Range: %s\n"
+                               "      CountInRange: %lu\n"
+                               "      PercentageInRange: %.2f\n"
+                               "      CumulativePercentageInRange: %.2f\n"
+                               "      TotalCapacityInRange: %lu %s\n"
+                               "      PercentCapacityInRange: %.2f\n"
+                               "      PercentCumulativeCapacityInRange: %.2f\n"
+                               "      MinValueInRange: %lu %s\n"
+                               "      MaxValueInRange: %lu %s\n",
+                               j, range_t,
+                               range_ptr->count_in_range, range_ptr->percentage,
+                               range_ptr->cumulative_percentage,
+                               range_ptr->total_in_range,
+                               (second_value_ts[0] != '\0') ? second_value_ts : value_ts,
+                               range_ptr->percent_in_range,
+                               range_ptr->percent_cumulative_in_range,
+                               range_ptr->min, value_ts,
+                               range_ptr->max, value_ts);
+               }
+       }
+
+       fprintf(yaml_fd, "\nTime frame reports for all users:\n");
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               struct ls3_stats_user_report_template *u_report_ptr =
+                               reports_with_stats->users_reports[i];
+
+               if (!u_report_ptr)
+                       continue;
+
+               fprintf(yaml_fd,
+                       "  - User: %s\n"
+                       "    UID: %lu\n"
+                       "    Tables:\n",
+                       ls3_stats_get_username_from_uid(u_report_ptr->uid),
+                       u_report_ptr->uid);
+
+               for (j = 0; j < LS3_STATS_USERS_REPORTS_COUNTER; j++) {
+                       char *title = ls3_stats_get_title_for_user_report(j);
+
+                       report_ptr = u_report_ptr->user_reports[j];
+                       if (report_ptr == NULL)
+                               LS3_FATAL("Unable to access the user report data: %d\n", j);
+
+                       value_ts = ls3_stats_get_str_value_type(report_ptr->value_type);
+                       second_value_ts =
+                               ls3_stats_get_str_value_type(report_ptr->second_value_type);
+
+                       fprintf(yaml_fd, "      - Title: %s\n", title);
+                       for (k = 0; k < report_ptr->count_ranges; k++) {
+                               char buffer[20];
+
+                               range_ptr = report_ptr->fs_ranges[k];
+                               if (range_ptr == NULL)
+                                       continue;
+
+                               ls3_stats_get_time_str(buffer,
+                                               range_ptr->last_time_access);
+                               fprintf(yaml_fd,
+                                       "        RangeStart: %lu %s\n"
+                                       "        RangeEnd: %lu %s\n"
+                                       "        CountFilesInRange: %lu\n"
+                                       "        MinSize: %lu %s\n"
+                                       "        MaxSize: %lu %s\n"
+                                       "        TotalSizeInRange: %lu %s\n"
+                                       "        LastTimeAccess: %s\n",
+                                       range_ptr->range_start, value_ts,
+                                       range_ptr->range_end, value_ts,
+                                       range_ptr->count_in_range,
+                                       range_ptr->min, second_value_ts,
+                                       range_ptr->max, second_value_ts,
+                                       range_ptr->total_in_range,
+                                       second_value_ts, buffer);
+                       }
+               }
+
+       }
+
+       fclose(yaml_fd);
+       printf("  Path to yaml: %s\n", reports_with_stats->format_report_file_name);
+}
+
+static void ls3_stats_print_to_out(const char *f_time, double e_time)
+{
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       char *second_value_ts;
+       char *value_ts;
+       FILE *out_fd;
+       int i, j, k;
+
+       ls3_stats_change_extension_in_file(LS3_STATS_FILE_EXTENSION_OUT);
+       out_fd = ls3_stats_open_file(reports_with_stats->format_report_file_name);
+
+       fprintf(out_fd, "Generated by LIPE stats v%.2f\n"
+               "Create Report Time: %s\n"
+               "Processed %lu files in %.0f secs\n"
+               "Device path: %s\n"
+               "Device name: %s\n"
+               "Device type: %s\n"
+               "Device size in KB: %lu\n"
+               "Client mount path: %s\n"
+               "RESULTS MAY BE INCOMPLETE\n\n",
+               LS3_STATS_VERSION, f_time,
+               reports_with_stats->total_count_files, e_time,
+               reports_with_stats->device_path,
+               reports_with_stats->device_name,
+               reports_with_stats->device_is_mdt ? "MDT" : "OST",
+               reports_with_stats->device_size >> 10,
+               reports_with_stats->client_mount_path);
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               char *header_text;
+
+               report_ptr = reports_with_stats->reports[i];
+               if (report_ptr == NULL)
+                       LS3_FATAL("Unable to access the report data: %d\n", i);
+
+               if (report_ptr->files_count == 0)
+                       continue; /* Nothing for print */
+
+               value_ts = ls3_stats_get_str_value_type(report_ptr->value_type);
+               second_value_ts =
+                       ls3_stats_get_str_value_type(report_ptr->second_value_type);
+               header_text = ls3_stats_get_header_text(i);
+
+               fprintf(out_fd, "%s\nGeneral: Count: %lu | Min value in ranges:"
+                       " %lu %s | Max value in ranges: %lu %s| Average value in ranges:"
+                       " %lu %s | Total value: %lu %s\n\n",
+                       header_text, report_ptr->files_count, report_ptr->min,
+                       value_ts, report_ptr->max, value_ts, report_ptr->avg,
+                       value_ts, report_ptr->total_value,
+                       (second_value_ts[0] != '\0') ? second_value_ts : value_ts);
+
+               fprintf(out_fd, "[------------- %1d ------------] [---- %1d -----]"
+                       " [-- %1d --] [------- %1d --------] [-------- %1d --------]"
+                       " [-- %1d --] [------- %1d --------] [-------- %1d --------]"
+                       " [-------- %1d --------]\n", 0, 1, 2, 3, 4, 5, 6, 7, 8);
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       char range_t[30];
+
+                       range_ptr = report_ptr->fs_ranges[j];
+                       if (range_ptr == NULL)
+                               continue;
+
+                       if (range_ptr->count_in_range == 0)
+                               continue;       /* Nothing for print */
+
+                       /* Different type of "range" field */
+                       ls3_stats_get_range_str(range_ptr, range_t, j, i, value_ts);
+
+                       fprintf(out_fd, "[ %s] [ %11lu] [%6.2f%%]"
+                               " [%6.2f%% cumulative] [%11lu %7s] [%6.2f%%]"
+                               " [%6.2f%% cumulative] [%11lu %7s] [%11lu %7s]\n",
+                               range_t, range_ptr->count_in_range,
+                               range_ptr->percentage,
+                               range_ptr->cumulative_percentage,
+                               range_ptr->total_in_range,
+                               (second_value_ts[0] != '\0') ? second_value_ts : value_ts,
+                               range_ptr->percent_in_range,
+                               range_ptr->percent_cumulative_in_range,
+                               range_ptr->min, value_ts,
+                               range_ptr->max, value_ts);
+               }
+
+               fprintf(out_fd, "[----------------------------] [------------]"
+                       " [-------] [------------------] [-------------------]"
+                       " [-------] [------------------] [-------------------]"
+                       " [-------------------]\n\n");
+       }
+
+
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               struct ls3_stats_user_report_template *u_report_ptr =
+                               reports_with_stats->users_reports[i];
+
+               if (!u_report_ptr)
+                       continue;
+
+               fprintf(out_fd, "\n\n[ user:%s   uid:%lu ]\n",
+                       ls3_stats_get_username_from_uid(u_report_ptr->uid),
+                       u_report_ptr->uid);
+               for (j = 0; j < LS3_STATS_USERS_REPORTS_COUNTER; j++) {
+                       char *title = ls3_stats_get_title_for_user_report(j);
+
+                       report_ptr = u_report_ptr->user_reports[j];
+                       if (report_ptr == NULL)
+                               LS3_FATAL("Unable to access the user report data: %d\n", j);
+
+                       value_ts =
+                               ls3_stats_get_str_value_type(report_ptr->value_type);
+                       second_value_ts =
+                               ls3_stats_get_str_value_type(report_ptr->second_value_type);
+                       fprintf(out_fd, "[-----------------------------"
+                               "--------------------------------------"
+                               "--------------------------------------"
+                               "----------------------------]\n");
+                       fprintf(out_fd, "[ %50s  %79s ]\n", title, " " );
+                       fprintf(out_fd, "[-----------------------------"
+                               "--------------------------------------"
+                               "--------------------------------------"
+                               "----------------------------]\n");
+                       fprintf(out_fd, "[--------Range by day--------]"
+                               " [-Count files-] [------Min size-----]"
+                               " [------Max size-----] [----Total size-----]"
+                               " [--Last time access--]\n");
+
+                       for (k = 0; k < report_ptr->count_ranges; k++) {
+                               char buffer[20];
+
+                               range_ptr = report_ptr->fs_ranges[k];
+                               if (range_ptr == NULL)
+                                       continue;
+
+                               ls3_stats_get_time_str(buffer,
+                                               range_ptr->last_time_access);
+
+                               fprintf(out_fd, "[ %8lu - %8lu %7s] [ %11lu ]"
+                                       " [%11lu %7s] [%11lu %7s]"
+                                       " [%11lu %7s] [%20s]\n",
+                                       range_ptr->range_start,
+                                       range_ptr->range_end, value_ts,
+                                       range_ptr->count_in_range,
+                                       range_ptr->min, second_value_ts,
+                                       range_ptr->max, second_value_ts,
+                                       range_ptr->total_in_range,
+                                       second_value_ts, buffer);
+                       }
+                       fprintf(out_fd, "[----------------------------]"
+                               " [-------------] [-------------------]"
+                               " [-------------------] [-------------------]"
+                               " [--------------------]\n\n");
+               }
+       }
+
+       fclose(out_fd);
+       printf("  Path to out: %s\n", reports_with_stats->format_report_file_name);
+}
+
+static void ls3_stats_print_to_csv(const char *f_time, double e_time)
+{
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       FILE *csv_fd;
+       int i, j, k;
+
+       ls3_stats_change_extension_in_file(LS3_STATS_FILE_EXTENSION_CSV);
+       csv_fd = ls3_stats_open_file(reports_with_stats->format_report_file_name);
+
+       fprintf(csv_fd, "Generated by LIPE stats v%.2f\n"
+               "Create Report Time [%s]\n"
+               "Processed %lu files in %.0f secs\n"
+               "Device path [%s]\n"
+               "Device name [%s]\n"
+               "Device type [%s]\n"
+               "Device size in KB [%lu]\n"
+               "Client mount path [%s]\n\n",
+               LS3_STATS_VERSION, f_time,
+               reports_with_stats->total_count_files, e_time,
+               reports_with_stats->device_path,
+               reports_with_stats->device_name,
+               reports_with_stats->device_is_mdt ? "MDT" : "OST",
+               reports_with_stats->device_size >> 10,
+               reports_with_stats->client_mount_path);
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               char *value_ts;
+               char *title_text;
+               char *second_value_ts;
+
+               report_ptr = reports_with_stats->reports[i];
+               if (report_ptr == NULL)
+                       LS3_FATAL("Unable to access the report data: %d\n", i);
+
+               if (report_ptr->files_count == 0)
+                       continue; /* Nothing for print */
+
+               value_ts = ls3_stats_get_str_value_type(report_ptr->value_type);
+               second_value_ts =
+                       ls3_stats_get_str_value_type(report_ptr->second_value_type);
+               title_text = ls3_stats_get_title_text(i);
+
+               fprintf(csv_fd, "Histogram,%s\n", title_text);
+               fprintf(csv_fd, "General,\nCount,%lu\nMinValueInRanges,%lu,%s\n"
+                       "MaxValueInRanges,%lu,%s\nAverageValueInRanges,%lu,%s\n"
+                       "TotalValue,%lu,%s\n\n",
+                       report_ptr->files_count, report_ptr->min,
+                       value_ts, report_ptr->max, value_ts, report_ptr->avg,
+                       value_ts, report_ptr->total_value,
+                       (second_value_ts[0] != '\0') ? second_value_ts : value_ts);
+
+               fprintf(csv_fd, "Range,RangeCount,"
+                       "RangePercentage,RangeCumulativePercentage,TotalInRange,"
+                       "PercentInRange,PercentCumulativeInRange,RangeMin,RangeMax\n");
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       char range_t[30];
+
+                       range_ptr = report_ptr->fs_ranges[j];
+                       if (range_ptr == NULL)
+                               continue;
+
+                       if (range_ptr->count_in_range == 0)
+                               continue;       /* Nothing for print */
+
+                       /* Different type of "range" field */
+                       ls3_stats_get_range_str(range_ptr, range_t, j, i, value_ts);
+                       fprintf(csv_fd, "%s,%lu,%.2f,%.2f,%lu %s,%.2f,%.2f,%lu %s,%lu %s\n",
+                               range_t, range_ptr->count_in_range, range_ptr->percentage,
+                               range_ptr->cumulative_percentage, range_ptr->total_in_range,
+                               (second_value_ts[0] != '\0') ? second_value_ts : value_ts,
+                               range_ptr->percent_in_range,
+                               range_ptr->percent_cumulative_in_range,
+                               range_ptr->min, value_ts, range_ptr->max, value_ts);
+               }
+               fprintf(csv_fd, "\n");
+               fprintf(csv_fd, "--------------------------------------------"
+                       "----------------------------------------------------"
+                       "---------------------------------\n");
+       }
+
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               struct ls3_stats_user_report_template *u_report_ptr =
+                               reports_with_stats->users_reports[i];
+
+               if (!u_report_ptr)
+                       continue;
+
+               fprintf(csv_fd, "\n\nHistogram,\nuser,%s\nuid,%lu\n",
+                       ls3_stats_get_username_from_uid(u_report_ptr->uid),
+                       u_report_ptr->uid);
+
+               for (j = 0; j < LS3_STATS_USERS_REPORTS_COUNTER; j++) {
+                       char *title = ls3_stats_get_title_for_user_report(j);
+
+                       report_ptr = u_report_ptr->user_reports[j];
+                       if (report_ptr == NULL)
+                               LS3_FATAL("Unable to access the user report data: %d\n", j);
+
+                       fprintf(csv_fd, "\n%s\n", title );
+                       fprintf(csv_fd, "RangeDayStart,RangeDayEnd,CountFilesInRange,"
+                               "MinSizeKB,MaxSizeKB,TotalSizeInRangeKB,LastTimeAccess\n");
+
+                       for (k = 0; k < report_ptr->count_ranges; k++) {
+                               char buffer[20];
+
+                               range_ptr = report_ptr->fs_ranges[k];
+                               if (range_ptr == NULL)
+                                       continue;
+
+                               ls3_stats_get_time_str(buffer,
+                                               range_ptr->last_time_access);
+
+                               fprintf(csv_fd, "%lu,%lu,%lu,%lu,%lu,%lu,%s\n",
+                                       range_ptr->range_start,
+                                       range_ptr->range_end,
+                                       range_ptr->count_in_range,
+                                       range_ptr->min, range_ptr->max,
+                                       range_ptr->total_in_range, buffer);
+                       }
+               }
+               fprintf(csv_fd, "--------------------------------------------"
+                       "----------------------------------------------------"
+                       "---------------------------------\n");
+       }
+
+       fclose(csv_fd);
+       printf("  Path to csv: %s\n", reports_with_stats->format_report_file_name);
+}
+
+/* ls3_stats_printf - Generates a report file for the
+ * user with the required extension
+ */
+void ls3_stats_printf(void)
+{
+       time_t current_time = time(NULL);
+       double elapsed_time;
+       struct tm *time_info;
+       char formatted_time[20];
+
+       elapsed_time = difftime(current_time, reports_with_stats->start_time);
+       time_info = localtime(&current_time);
+       strftime(formatted_time, sizeof(formatted_time),
+                "%Y-%m-%d %H:%M:%S", time_info);
+
+       /* Do all the necessary calculations */
+       ls3_stats_calculate_values();
+       ls3_stats_prepare_file();
+
+       printf("File with the results:\n");
+
+       if (reports_with_stats->report_extension & LS3_STATS_FILE_EXTENSION_OUT)
+               ls3_stats_print_to_out(formatted_time, elapsed_time);
+
+       if (reports_with_stats->report_extension & LS3_STATS_FILE_EXTENSION_YAML)
+               ls3_stats_print_to_yaml(formatted_time, elapsed_time);
+
+       if (reports_with_stats->report_extension & LS3_STATS_FILE_EXTENSION_JSON)
+               ls3_stats_print_to_json(formatted_time, elapsed_time);
+
+       if (reports_with_stats->report_extension & LS3_STATS_FILE_EXTENSION_CSV)
+               ls3_stats_print_to_csv(formatted_time, elapsed_time);
+
+}
+
+/* ls3_stats_calculate_values - makes additional calculations based on all
+ * collected data for each table.
+ */
+void ls3_stats_calculate_values(void)
+{
+       int i, j;
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               double cumulative_percentage = 0;
+               double percent_cumulative_in_range = 0;
+
+               report_ptr = reports_with_stats->reports[i];
+               if (report_ptr == NULL)
+                       LS3_FATAL("Unable to access the report data: %d\n", i);
+
+               if (report_ptr->files_count == 0)
+                       continue;
+
+               if (i == LS3_STATS_FILES_SIZE)
+                       reports_with_stats->total_count_files =
+                               report_ptr->files_count;
+
+               report_ptr->avg =
+                       report_ptr->total_value / report_ptr->files_count;
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       range_ptr = report_ptr->fs_ranges[j];
+                       if (range_ptr == NULL)
+                               continue;
+
+                       if (range_ptr->count_in_range == 0)
+                               continue;       /* Nothing for calculate */
+
+                       range_ptr->percentage =
+                               ((double)range_ptr->count_in_range /
+                               (double)report_ptr->files_count) * 100;
+
+                       report_ptr->fs_ranges[j]->cumulative_percentage =
+                               cumulative_percentage + range_ptr->percentage;
+
+                       cumulative_percentage += range_ptr->percentage;
+
+                       range_ptr->percent_in_range =
+                               ((double)range_ptr->total_in_range /
+                               (double)report_ptr->total_value) * 100;
+
+                       range_ptr->percent_cumulative_in_range =
+                               percent_cumulative_in_range +
+                               range_ptr->percent_in_range;
+
+                       percent_cumulative_in_range +=
+                               range_ptr->percent_in_range;
+               }
+       }
+
+       if (reports_with_stats->device_path)
+               reports_with_stats->device_size =
+                       ls3_stats_get_dev_size(reports_with_stats->device_path);
+}
+
+static struct range_report_template *ls3_stats_get_new_range_ptr(uint64_t res)
+{
+       struct range_report_template *range_ptr;
+
+       range_ptr = xcalloc(1, sizeof(struct range_report_template));
+       range_ptr->min = UINT64_MAX;
+       if (res >= 2) {
+               range_ptr->range_start = (int)pow(2, floor(log2(res)));
+               range_ptr->range_end = range_ptr->range_start * 2;
+       } else {
+               range_ptr->range_start = 0;
+               range_ptr->range_end = 2;
+       }
+
+       return range_ptr;
+}
+
+static struct report_template *ls3_stats_get_new_report_ptr(void)
+{
+       struct report_template *report_ptr =
+               xcalloc(1, sizeof(struct report_template));
+
+       report_ptr->count_ranges = LS3_STATS_COUNT_RANGE_BY_DEFAULT;
+       report_ptr->min = UINT64_MAX;
+       report_ptr->total_value = 0;
+       report_ptr->curent_max_range = pow(2, report_ptr->count_ranges);
+       report_ptr->fs_ranges = (struct range_report_template**)xcalloc(
+                                report_ptr->count_ranges,
+                                sizeof(struct range_report_template*));
+       pthread_mutex_init(&report_ptr->report_template_mutex, NULL);
+       return report_ptr;
+}
+
+static void ls3_stats_expand_num_of_ranges(struct report_template *report_ptr,
+       int index_range)
+{
+       struct range_report_template **new_ranges_array;
+       unsigned int old_size = report_ptr->count_ranges;
+       size_t num_bytes_to_clear;
+
+       report_ptr->count_ranges = index_range + 1;
+       new_ranges_array = xrealloc(report_ptr->fs_ranges,
+                                   report_ptr->count_ranges *
+                                   sizeof(struct range_report_template*));
+
+       report_ptr->fs_ranges = new_ranges_array;
+       num_bytes_to_clear = (report_ptr->count_ranges - old_size) *
+                             sizeof(struct range_report_template*);
+       memset(report_ptr->fs_ranges + old_size, 0,
+               num_bytes_to_clear);
+}
+
+static void ls3_stats_update_range(ls3_stats_report_type report_type,
+       int64_t range_value, int64_t second_value)
+{
+       int index_range;
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       ls3_stats_val_type value_type;
+       uint64_t result_range_value;
+       uint64_t result_second_value = 0;
+
+       report_ptr = reports_with_stats->reports[report_type];
+       if (!report_ptr)
+               LS3_FATAL("Unable to access the report data: %d\n", report_type);
+
+       value_type = ls3_stats_get_value_type(report_type);
+       switch (value_type)
+       {
+               case LS3_STATS_VALUE_TYPE_KB:
+                       result_range_value = range_value >> 10;
+                       break;
+               case LS3_STATS_VALUE_TYPE_DAYS:
+                       result_range_value = ls3_stats_get_day_diff(range_value);
+                       result_second_value = second_value >> 10;
+                       report_ptr->second_value_type = LS3_STATS_VALUE_TYPE_KB;
+                       break;
+               default:
+                       result_range_value = range_value;
+                       report_ptr->second_value_type =
+                               LS3_STATS_VALUE_TYPE_EMPTY;
+                       result_second_value = 0;
+                       break;
+       }
+
+       report_ptr->value_type = value_type;
+       index_range = ls3_stats_get_range_index(result_range_value);
+
+       pthread_mutex_lock(&report_ptr->report_template_mutex);
+       if (result_range_value < report_ptr->min)
+               report_ptr->min = result_range_value;
+
+       if (result_range_value > report_ptr->max) {
+               report_ptr->max = result_range_value;
+               report_ptr->curent_max_range = pow(2, report_ptr->max);
+
+               if (index_range > report_ptr->count_ranges - 1)
+                       ls3_stats_expand_num_of_ranges(report_ptr, index_range);
+       }
+
+       range_ptr = report_ptr->fs_ranges[index_range];
+       if (!range_ptr) {
+               range_ptr = ls3_stats_get_new_range_ptr(result_range_value);
+               report_ptr->fs_ranges[index_range] = range_ptr;
+       }
+
+       switch (report_type)
+       {
+               case LS3_STATS_TIME_SINCE_LAST_MOD_RF:
+               case LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF:
+               case LS3_STATS_TIME_SINCE_LAST_ACCESS_RF:
+               case LS3_STATS_TIME_SINCE_LAST_CREATION_RF:
+                       /* When the range is time and the value
+                        * is expressed in size */
+                       range_ptr->total_in_range += result_second_value;
+                       report_ptr->total_value += result_second_value;
+                       break;
+               default:
+                       /* When range and value have same type for print */
+                       range_ptr->total_in_range += result_range_value;
+                       report_ptr->total_value += result_range_value;
+                       break;
+       }
+
+       range_ptr->count_in_range++;
+       report_ptr->files_count++;
+
+       if (result_range_value < range_ptr->min)
+               range_ptr->min = result_range_value;
+
+       if (result_range_value > range_ptr->max)
+               range_ptr->max = result_range_value;
+
+
+       pthread_mutex_unlock(&report_ptr->report_template_mutex);
+}
+
+
+static struct range_report_template *ls3_stats_find_id_in_ranges(uint64_t id,
+       struct report_template *report_ptr)
+{
+       struct range_report_template *range_ptr;
+       int i;
+
+       for (i = 0; i <= report_ptr->last_idx_in_fs_ranges; i++) {
+               range_ptr = report_ptr->fs_ranges[i];
+               if (range_ptr == NULL && i != 0)
+                       LS3_FATAL("Unable to access the UID/PID/PRJID %lu\n",
+                                 id);
+               else if (range_ptr == NULL && i == 0)
+                       return NULL;    /* When first file */
+
+               if (range_ptr->rrt_id == id)
+                       return range_ptr;
+       }
+
+       return NULL;
+}
+
+static void ls3_stats_update_range_with_id(ls3_stats_report_type report_type,
+       int64_t id, int64_t value)
+{
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       uint64_t result_second_value;
+       uint64_t current_index;
+       int64_t local_id = id;
+
+       report_ptr = reports_with_stats->reports[report_type];
+       if (!report_ptr)
+               LS3_FATAL("Unable to access the report data: %d\n", report_type);
+
+       /* LS3_STATS_STRIPE_SIZE Will display in KB
+        * (+ exception redundant pointer allocations) */
+       if (report_type == LS3_STATS_STRIPE_SIZE)
+               local_id = local_id >> 10;
+
+       /* Get size in KB (here it's only for users & groups) */
+       result_second_value = value >> 10;
+       report_ptr->value_type = LS3_STATS_VALUE_TYPE_KB;
+
+       pthread_mutex_lock(&report_ptr->report_template_mutex);
+       if (result_second_value < report_ptr->min)
+               report_ptr->min = result_second_value;
+
+       if (result_second_value > report_ptr->max) {
+               report_ptr->max = result_second_value;
+               report_ptr->curent_max_range = local_id;
+       }
+
+       range_ptr = ls3_stats_find_id_in_ranges(local_id, report_ptr);
+       if (!range_ptr) {
+               /* Get exactly index in the array since it's a new UID/PID/PRJID */
+               current_index = report_ptr->last_idx_in_fs_ranges + 1;
+               if (current_index == 1 && report_ptr->fs_ranges[0] == NULL)
+                       current_index = 0;      /* This is first UID/PID/PRJID */
+
+               if (current_index == report_ptr->count_ranges)
+                       ls3_stats_expand_num_of_ranges(report_ptr,
+                                                      report_ptr->count_ranges +
+                                                      LS3_STATS_COUNT_RANGE_BY_DEFAULT);
+
+               range_ptr = report_ptr->fs_ranges[current_index];
+               if (!range_ptr) {
+                       range_ptr =
+                               ls3_stats_get_new_range_ptr(LS3_STATS_EMPTY_VALUE);
+                       range_ptr->rrt_id = local_id;
+                       report_ptr->fs_ranges[current_index] = range_ptr;
+               }
+               report_ptr->last_idx_in_fs_ranges = current_index;
+       }
+
+       range_ptr->total_in_range += result_second_value;
+       range_ptr->count_in_range++;
+       report_ptr->total_value += result_second_value;
+       report_ptr->files_count++;
+
+       if (result_second_value < range_ptr->min)
+               range_ptr->min = result_second_value;
+
+       if (result_second_value > range_ptr->max)
+               range_ptr->max = result_second_value;
+
+       pthread_mutex_unlock(&report_ptr->report_template_mutex);
+}
+
+/* Adds a new user to the stats reporting structure, allocating and expanding
+ * resources as needed.
+ *
+ * @param user_id  The user ID to add.
+ * @return         Pointer to the ls3_stats_user_report_template.
+ */
+static struct ls3_stats_user_report_template
+       *ls3_stats_add_new_user(uint64_t user_id)
+{
+       struct ls3_stats_user_report_template *user_ptr;
+       uint64_t current_index = 0;
+       int i;
+
+       /* Get exactly index in the array since it's a new user */
+       current_index = reports_with_stats->last_user_idx_in_array + 1;
+       if (current_index == 1 && reports_with_stats->users_reports[0] == NULL)
+               current_index = 0;      /* This is first user with 0 index */
+
+       if (current_index == reports_with_stats->max_count_users_in_report) {
+               struct ls3_stats_user_report_template **new_ranges_array;
+               unsigned int old_size =
+                       reports_with_stats->max_count_users_in_report;
+               size_t num_bytes_to_clear;
+               size_t tmp_sizeof_urt =
+                       sizeof(struct ls3_stats_user_report_template*);
+
+               /* current max value + default size (step 100 entries) */
+               reports_with_stats->max_count_users_in_report =
+                       reports_with_stats->max_count_users_in_report +
+                       LS3_STATS_ARRAY_SIZE_BY_DEFAULT;
+
+               new_ranges_array =
+                       xrealloc(reports_with_stats->users_reports,
+                                reports_with_stats->max_count_users_in_report *
+                                tmp_sizeof_urt);
+
+               reports_with_stats->users_reports = new_ranges_array;
+               num_bytes_to_clear =
+                       (reports_with_stats->max_count_users_in_report -
+                        old_size) *
+                        sizeof(struct ls3_stats_user_report_template*);
+
+               memset(reports_with_stats->users_reports + old_size, 0,
+               num_bytes_to_clear);
+       }
+
+       user_ptr = reports_with_stats->users_reports[current_index];
+       if (!user_ptr) {
+               user_ptr =
+                       xcalloc(1, sizeof(struct ls3_stats_user_report_template));
+               user_ptr->uid = user_id;
+               for (i = 0; i < LS3_STATS_USERS_REPORTS_COUNTER; i++) {
+                       struct report_template *tmp_report_ptr =
+                               ls3_stats_get_new_report_ptr();
+
+                       tmp_report_ptr->value_type = LS3_STATS_VALUE_TYPE_DAYS;
+                       tmp_report_ptr->second_value_type =
+                               LS3_STATS_VALUE_TYPE_KB;
+                       user_ptr->user_reports[i] = tmp_report_ptr;
+               }
+       }
+
+       reports_with_stats->users_reports[current_index] = user_ptr;
+       reports_with_stats->last_user_idx_in_array = current_index;
+
+       return user_ptr;
+}
+
+/* Finds and returns the user report data for a given user_id.
+ * Returns NULL if the user_report for user_id is not found.
+ *
+ * @param user_id  The user ID to search for.
+ * @return         Pointer to the ls3_stats_user_report_template data if found,
+ *                otherwise NULL.
+ */
+static struct ls3_stats_user_report_template
+       *ls3_stats_find_user_reports(uint64_t user_id)
+{
+       struct ls3_stats_user_report_template *user_ptr;
+       int i;
+
+       /* Up to a couple of thousand elements in an array,
+        * looping through the array is faster than checking the user
+        * in a bitmap with values up to 1B (before 3k elements).
+        * Performance degrades ifthe array is larger than 5k elements,
+        * the difference is in microseconds (for 4к users different:
+        * Bitmap - 0.000005 sec | Array 0.000009 sec).
+        * But this saves 125MB of RAM on bitmap.
+        * But such cases when > 3000 users must probably be very rare. */
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               user_ptr = reports_with_stats->users_reports[i];
+               if (user_ptr == NULL && i != 0)
+                       LS3_FATAL("Unable to access the user %lu report data\n",
+                                 user_id);
+               else if (user_ptr == NULL && i == 0)
+                       return NULL;    /* When first file */
+
+               if (user_ptr->uid == user_id)
+                       return user_ptr;
+       }
+
+       return NULL;
+}
+
+static void ls3_stats_update_user_report_range(ls3_stats_users_reports report_type,
+       uint64_t user_id, int64_t range, int64_t size)
+{
+       struct ls3_stats_user_report_template *user_report_ptr;
+       struct report_template *report_ptr;
+       struct range_report_template *range_ptr;
+       uint64_t result_second_value = size >> 10;
+       unsigned int result_range_value;
+       unsigned int index_range;
+
+       if (report_type >= LS3_STATS_USERS_REPORTS_COUNTER)
+               LS3_FATAL("Defunct report type LS3_STATS_USERS_REPORTS_%d\n",
+                         report_type);
+
+       user_report_ptr = ls3_stats_find_user_reports(user_id);
+       if (!user_report_ptr) {
+               pthread_mutex_lock(&reports_with_stats->user_rt_mutex);
+               user_report_ptr = ls3_stats_add_new_user(user_id);
+               pthread_mutex_unlock(&reports_with_stats->user_rt_mutex);
+       }
+
+       if (!user_report_ptr)
+               LS3_FATAL("Unable to access the user [%lu] report data:\n",
+                         user_id);
+
+       report_ptr = user_report_ptr->user_reports[report_type];
+       if (!report_ptr)
+               LS3_FATAL("Unable to access the user report data: %d\n",
+                         report_type);
+
+       /* Here the range is calculated in days only */
+       result_range_value = ls3_stats_get_day_diff(range);
+       index_range = ls3_stats_get_range_index(result_range_value);
+
+       pthread_mutex_lock(&report_ptr->report_template_mutex);
+       if (result_range_value < report_ptr->min)
+               report_ptr->min = result_range_value;
+
+       if (result_range_value > report_ptr->max) {
+               report_ptr->max = result_range_value;
+               report_ptr->curent_max_range = pow(2, report_ptr->max);
+
+               if (index_range > report_ptr->count_ranges - 1)
+                       ls3_stats_expand_num_of_ranges(report_ptr, index_range);
+       }
+
+       range_ptr = report_ptr->fs_ranges[index_range];
+       if (!range_ptr) {
+               range_ptr = ls3_stats_get_new_range_ptr(result_range_value);
+               report_ptr->fs_ranges[index_range] = range_ptr;
+       }
+
+       range_ptr->total_in_range += result_second_value;
+       range_ptr->count_in_range++;
+       report_ptr->total_value += result_second_value;
+       report_ptr->files_count++;
+
+       if (range > range_ptr->last_time_access)
+               range_ptr->last_time_access = range;
+
+       if (result_second_value < range_ptr->min)
+               range_ptr->min = result_second_value;
+
+       if (result_second_value > range_ptr->max)
+               range_ptr->max = result_second_value;
+
+       pthread_mutex_unlock(&report_ptr->report_template_mutex);
+}
+
+
+int ls3_stats_update_info(struct ls3_object_attrs *loa_all)
+{
+       struct lipe_path_entry *lpe;
+       uint64_t allocate_file_size = 0;
+       uint64_t alligned_size = 0;             /* relative to block size */
+       uint16_t block_size = 512;
+       const char *file_name;
+
+       allocate_file_size = loa_all->loa_blocks * block_size;
+       alligned_size = loa_all->loa_size + block_size - 1;
+
+       /* In most cases loa_all->loa_blocks is not available on MDT. */
+       if (!reports_with_stats->device_is_mdt) {
+               /* Get equal overhead (Files whose size is equal
+                * than capacity used. */
+               if (allocate_file_size == alligned_size)
+                       ls3_stats_update_range(LS3_STATS_EQUAL_OVERHEAD,
+                                              loa_all->loa_size,
+                                              LS3_STATS_EMPTY_VALUE);
+               /* Get positive overhead (Files whose capacity used is
+                * larger than to its size. */
+               else if (allocate_file_size - alligned_size > 0)
+                       ls3_stats_update_range(LS3_STATS_POSITIVE_OVERHEAD,
+                                              allocate_file_size -
+                                              loa_all->loa_size,
+                                              LS3_STATS_EMPTY_VALUE);
+               /* Get negative overhead (Files whose size is larger than
+                * capacity used. Includes directories, symlinks and
+                * regular files). */
+               else if (alligned_size - allocate_file_size > 0)
+                       ls3_stats_update_range(LS3_STATS_NEGATIVE_OVERHEAD,
+                                              loa_all->loa_size -
+                                              allocate_file_size,
+                                              LS3_STATS_EMPTY_VALUE);
+       }
+
+       /* Get filename length (Includes regular files, dirs, symlinks
+        * and special files) */
+       lipe_list_for_each_entry(lpe, &loa_all->loa_paths, lpe_linkage) {
+               file_name = ls3_stats_get_filename_from_path(lpe->lpe_path);
+               ls3_stats_update_range(LS3_STATS_FILENAME_LENGTH,
+                                      (int16_t)strlen(file_name),
+                                      LS3_STATS_EMPTY_VALUE);
+       }
+
+       /* Reports only for regular file's */
+       if (!(loa_all->loa_mode & S_IFREG))
+               return 0;
+
+       ls3_stats_update_range(LS3_STATS_FILES_SIZE, loa_all->loa_size,
+                              LS3_STATS_EMPTY_VALUE);
+       ls3_stats_update_range(LS3_STATS_TIME_SINCE_LAST_MOD_RF,
+                              loa_all->loa_mtime, loa_all->loa_size);
+       ls3_stats_update_range(LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF,
+                              loa_all->loa_ctime, loa_all->loa_size);
+       ls3_stats_update_range(LS3_STATS_TIME_SINCE_LAST_CREATION_RF,
+                              loa_all->loa_crtime, loa_all->loa_size);
+       ls3_stats_update_range(LS3_STATS_TIME_SINCE_LAST_ACCESS_RF,
+                              loa_all->loa_atime, loa_all->loa_size);
+       ls3_stats_update_range_with_id(LS3_STATS_STORAGE_SET_SIZE_BY_USER,
+                                      loa_all->loa_uid,
+                                      loa_all->loa_size);
+       ls3_stats_update_range_with_id(LS3_STATS_STORAGE_SET_SIZE_BY_GROUP,
+                                      loa_all->loa_gid,
+                                      loa_all->loa_size);
+       ls3_stats_update_range_with_id(LS3_STATS_STORAGE_SET_SIZE_BY_PROJID,
+                                      loa_all->loa_projid,
+                                      loa_all->loa_size);
+       ls3_stats_update_user_report_range(LS3_STATS_USERS_REPORTS_MOD_RF,
+                                          loa_all->loa_uid,
+                                          loa_all->loa_mtime,
+                                          loa_all->loa_size);
+       ls3_stats_update_user_report_range(LS3_STATS_USERS_REPORTS_MD_MOD_RF,
+                                          loa_all->loa_uid,
+                                          loa_all->loa_ctime,
+                                          loa_all->loa_size);
+       ls3_stats_update_user_report_range(LS3_STATS_USERS_REPORTS_CREATION_RF,
+                                          loa_all->loa_uid,
+                                          loa_all->loa_crtime,
+                                          loa_all->loa_size);
+       ls3_stats_update_user_report_range(LS3_STATS_USERS_REPORTS_ACCESS_RF,
+                                          loa_all->loa_uid,
+                                          loa_all->loa_atime,
+                                          loa_all->loa_size);
+
+       if (reports_with_stats->device_is_mdt) {
+               uint16_t mirror_count = 0;
+               uint64_t stripe_count = 0;
+               uint64_t stripe_size = 0;
+               int rc;
+
+               if (!loa_all->loa_layout)
+                       return 0;
+
+               rc = llapi_layout_get_last_init_comp(loa_all->loa_layout);
+               if (rc < 0)
+                       return -1; /* TODO add text message in report file */
+
+               rc = llapi_layout_mirror_count_get(loa_all->loa_layout,
+                                                  &mirror_count);
+               if (rc < 0)
+                       return -1;
+
+               rc = llapi_layout_stripe_count_get(loa_all->loa_layout,
+                                                  &stripe_count);
+               if (rc < 0)
+                       return -1;
+
+               rc = llapi_layout_stripe_size_get(loa_all->loa_layout,
+                                                 &stripe_size);
+               if (rc < 0)
+                       return -1;
+
+               ls3_stats_update_range(LS3_STATS_STRIPE_COUNT, stripe_count,
+                                      LS3_STATS_EMPTY_VALUE);
+
+               ls3_stats_update_range_with_id(LS3_STATS_MIRROR_COUNT,
+                                              mirror_count, loa_all->loa_size);
+
+               ls3_stats_update_range_with_id(LS3_STATS_STRIPE_SIZE,
+                                              stripe_size, loa_all->loa_size);
+
+               /* TODO: Get info about compression (lvl/type/chunk_log_bits)
+                * from loa_all->loa_layout via struct llapi_layout_comp */
+       } else {
+               struct filter_fid ptr_filter_fid;
+               struct ost_layout ptr_ost_layout;
+
+               ptr_filter_fid = loa_all->loa_filter_fid;
+               ptr_ost_layout = ptr_filter_fid.ff_layout;
+               ls3_stats_update_range(LS3_STATS_STRIPE_COUNT,
+                                      ptr_ost_layout.ol_stripe_count,
+                                      LS3_STATS_EMPTY_VALUE);
+
+               ls3_stats_update_range_with_id(LS3_STATS_STRIPE_SIZE,
+                                              ptr_ost_layout.ol_stripe_size,
+                                              loa_all->loa_size);
+
+               ls3_stats_update_range(LS3_STATS_CAPACITY_USED,
+                                      allocate_file_size,
+                                      LS3_STATS_EMPTY_VALUE);
+       }
+
+       return 0;
+}
+
+void ls3_stats_init(void)
+{
+       int i;
+
+       reports_with_stats = xcalloc(1, sizeof(struct fstats_report));
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               struct report_template *report_ptr =
+                       ls3_stats_get_new_report_ptr();
+
+               reports_with_stats->reports[i] = report_ptr;
+       }
+
+       reports_with_stats->max_count_users_in_report =
+               LS3_STATS_ARRAY_SIZE_BY_DEFAULT;
+       reports_with_stats->users_reports =
+                       (struct ls3_stats_user_report_template**)xcalloc(
+                        reports_with_stats->max_count_users_in_report,
+                        sizeof(struct ls3_stats_user_report_template*));
+       reports_with_stats->last_user_idx_in_array = 0;
+
+
+       reports_with_stats->device_size = 0;
+       reports_with_stats->start_time = time(NULL);
+       pthread_mutex_init(&reports_with_stats->user_rt_mutex, NULL);
+}
+
+void ls3_stats_destroy(void)
+{
+       int i, j, k;
+
+       for (i = 0; i < LS3_STATS_TOTAL_COUNT_REPORT; i++) {
+               struct report_template *report_ptr =
+                       reports_with_stats->reports[i];
+
+               for (j = 0; j < report_ptr->count_ranges; j++) {
+                       struct range_report_template *range_ptr =
+                               report_ptr->fs_ranges[j];
+
+                       if (range_ptr != NULL)
+                               free(range_ptr);
+               }
+               pthread_mutex_destroy(&report_ptr->report_template_mutex);
+               free(report_ptr->fs_ranges);
+       }
+
+       for (i = 0; i <= reports_with_stats->last_user_idx_in_array; i++) {
+               struct ls3_stats_user_report_template *tmp_user_reports =
+                       reports_with_stats->users_reports[i];
+
+               if (tmp_user_reports == NULL)
+                       continue;
+
+               /* if have user, means there is at least one file
+                * and all reports */
+               for (j = 0; j < LS3_STATS_USERS_REPORTS_COUNTER; j++) {
+                       struct report_template *user_report =
+                               tmp_user_reports->user_reports[j];
+
+                       for (k = 0; k < user_report->count_ranges; k++) {
+                               struct range_report_template *range_ptr =
+                                       user_report->fs_ranges[k];
+
+                               if (range_ptr != NULL)
+                                       free(range_ptr);
+                       }
+                       free(user_report);
+               }
+               free(tmp_user_reports);
+       }
+
+       if (reports_with_stats->report_file_name)
+               free(reports_with_stats->report_file_name);
+
+       if (reports_with_stats->device_path)
+               free(reports_with_stats->device_path);
+
+       if (reports_with_stats->format_report_file_name)
+               free(reports_with_stats->format_report_file_name);
+
+       if (reports_with_stats->device_name)
+               free(reports_with_stats->device_name);
+
+       if (reports_with_stats->client_mount_path)
+               free(reports_with_stats->client_mount_path);
+
+       pthread_mutex_destroy(&reports_with_stats->user_rt_mutex);
+       free(reports_with_stats->users_reports);
+       free(reports_with_stats);
+}
diff --git a/lipe/src/lipe_scan3/ls3_stats.h b/lipe/src/lipe_scan3/ls3_stats.h
new file mode 100644 (file)
index 0000000..e414d76
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2023, DDN Storage Corporation.
+ *
+ * Author: Vitaliy Kuznetsov <vkuznetsov@ddn.com>
+ */
+
+#ifndef _LS3_STATS_H_
+#define _LS3_STATS_H_
+
+#include <math.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdbool.h>
+#include "ls3_debug.h"
+#include "ls3_object_attrs.h"
+
+#define LS3_STATS_VERSION 1.0
+#define LS3_STATS_EMPTY_VALUE 0
+#define LS3_STATS_COUNT_RANGE_BY_DEFAULT 10
+#define LS3_STATS_ARRAY_SIZE_BY_DEFAULT 100
+#define DEFAULT_DIR_FOR_REPORTS "/var/log/lustre/lipe/reports/"
+
+/* ls3_stats_file_extension - bitmap to select report extension */
+typedef enum {
+       LS3_STATS_FILE_EXTENSION_OUT  = 0x0001,
+       LS3_STATS_FILE_EXTENSION_JSON = 0x0002,
+       LS3_STATS_FILE_EXTENSION_CSV  = 0x0004,
+       LS3_STATS_FILE_EXTENSION_YAML = 0x0008,
+       LS3_STATS_FILE_EXTENSION_ALL  = 0x000f  /* By Default */
+} ls3_stats_file_extension;
+
+/* ls3_stats_users_reports - stores the number of time reports per user */
+typedef enum {
+       LS3_STATS_USERS_REPORTS_MOD_RF = 0,
+       LS3_STATS_USERS_REPORTS_MD_MOD_RF,
+       LS3_STATS_USERS_REPORTS_CREATION_RF,
+       LS3_STATS_USERS_REPORTS_ACCESS_RF,
+       LS3_STATS_USERS_REPORTS_COUNTER
+} ls3_stats_users_reports;
+
+typedef struct {
+       const char *extension_name;
+       ls3_stats_file_extension extension_value;
+} ls3_stats_extension_mapping;
+
+typedef enum {
+       LS3_STATS_FILES_SIZE = 0,
+       LS3_STATS_CAPACITY_USED,
+       LS3_STATS_EQUAL_OVERHEAD,
+       LS3_STATS_POSITIVE_OVERHEAD,
+       LS3_STATS_NEGATIVE_OVERHEAD,
+       LS3_STATS_DIRECTORY_SIZE_ENTRIES,
+       LS3_STATS_DIRECTORY_SIZE_KB,
+       LS3_STATS_LINK_COUNT,
+       LS3_STATS_FILENAME_LENGTH,
+       LS3_STATS_TIME_SINCE_LAST_MOD_RF,
+       LS3_STATS_TIME_SINCE_LAST_MD_MOD_RF,
+       LS3_STATS_TIME_SINCE_LAST_CREATION_RF,
+       LS3_STATS_TIME_SINCE_LAST_ACCESS_RF,
+       LS3_STATS_FILES_EMPLOYING_DOM,
+       LS3_STATS_STORAGE_SET_SIZE_BY_USER,
+       LS3_STATS_STORAGE_SET_SIZE_BY_GROUP,
+       LS3_STATS_STORAGE_SET_SIZE_BY_PROJID,
+       LS3_STATS_STRIPE_COUNT,
+       LS3_STATS_STRIPE_SIZE,
+       LS3_STATS_MIRROR_COUNT,
+       LS3_STATS_TOTAL_COUNT_REPORT            /* Should always be the last */
+} ls3_stats_report_type;
+
+typedef enum {
+       LS3_STATS_VALUE_TYPE_ERROR,
+       LS3_STATS_VALUE_TYPE_KB,
+       LS3_STATS_VALUE_TYPE_ENTRIES,
+       LS3_STATS_VALUE_TYPE_CHARS,
+       LS3_STATS_VALUE_TYPE_LINKS,
+       LS3_STATS_VALUE_TYPE_DAYS,
+       LS3_STATS_VALUE_TYPE_BYTES,
+       LS3_STATS_VALUE_TYPE_STRIPE,
+       LS3_STATS_VALUE_TYPE_MIRROR,
+       LS3_STATS_VALUE_TYPE_EMPTY
+} ls3_stats_val_type;
+
+struct range_report_template {
+       uint64_t rrt_id;                        /* UID/GID/PRJID/StripeSize */
+       uint64_t range_start;
+       uint64_t range_end;
+       uint64_t count_in_range;
+       uint64_t total_in_range;
+       uint64_t min;
+       uint64_t max;
+       /* last_time_access - only for users time report.
+        * Displays the last modified date of the files in the range */
+       uint64_t last_time_access;
+       double percentage;
+       double cumulative_percentage;
+       double percent_in_range;
+       double percent_cumulative_in_range;
+};
+
+struct report_template {
+       pthread_mutex_t report_template_mutex;
+       uint64_t files_count;                   /* total files count in report */
+       uint64_t total_value;                   /* size/chars/... in all range */
+       uint64_t min;
+       uint64_t max;
+       uint64_t avg;
+       uint64_t last_idx_in_fs_ranges;         /* current last idx in fs_ranges */
+       unsigned int curent_max_range;
+       unsigned int count_ranges;
+       ls3_stats_val_type value_type;          /* KB/entries/chars/links/days */
+       ls3_stats_val_type second_value_type;   /* KB/entries/chars/links/days */
+       struct range_report_template **fs_ranges;
+};
+
+/* Since collect-fsize-stats is an information output policy
+ * and filters work for it, where you can specify a specific
+ * user and get information about a specific user within the
+ * main report, but this does not reflect the big picture.
+ * This is necessary in order to show information about all
+ * users at once. */
+struct ls3_stats_user_report_template {
+       uint64_t uid;                           /* user ID */
+       struct report_template *user_reports[LS3_STATS_USERS_REPORTS_COUNTER];
+};
+
+struct fstats_report {
+       pthread_mutex_t user_rt_mutex;
+       time_t start_time;
+       uint64_t total_count_files;
+       uint64_t device_size;                   /* In bytes */
+       uint64_t max_count_users_in_report;     /* current max array size */
+       uint64_t last_user_idx_in_array;        /* current last idx in array */
+       int report_extension;                   /* bitmap for extension */
+       char *report_file_name;
+       char *format_report_file_name;
+       char *device_path;
+       char *device_name;
+       char *client_mount_path;
+       bool device_is_mdt;
+       struct report_template *reports[LS3_STATS_TOTAL_COUNT_REPORT];
+       struct ls3_stats_user_report_template **users_reports;
+};
+
+struct fstats_report *reports_with_stats;
+void ls3_stats_init(void);
+void ls3_stats_destroy(void);
+int ls3_stats_get_range_index(uint64_t num);
+uint64_t ls3_stats_get_dev_size(const char* device_path);
+int ls3_stats_update_info(struct ls3_object_attrs *loa_all);
+void ls3_stats_calculate_values(void);
+void ls3_stats_printf(void);
+
+#endif /* _LS3_STATS_H_ */
index 4316c77..446ea9a 100644 (file)
@@ -67,6 +67,7 @@
           print-json
           print-absolute-path
           print-relative-path
+          collect-fsize-stats
 ))
 
 (assert (catch '*lipe-no-context*
index ccdf620..319b012 100644 (file)
@@ -1071,6 +1071,42 @@ test_305() {
 }
 run_test 305 "print-json prints the right paths"
 
+# Test for --collect-fsize-stats policy
+test_306() {
+       local facet=mds1
+       local report_path="$MOUNT/files_size_report.yaml"
+       local file=$MOUNT/$tfile
+       local no_of_files
+       local total_size
+
+       init_lipe_scan3_env "$file"
+       fallocate -l 10M $file
+
+       [[ $? -ne 0 ]] && error "dd ended with an error"
+
+       sync
+
+       out=$(lipe_scan3_facet "$facet" --collect-fsize-stats="$report_path")
+
+       [[ $? -ne 0 ]] && error "lipe_scan3 collect-fsize-stats failed"
+
+       [[ -f "$report_path" ]] || error "File with report not found."
+
+       verify_yaml $report_path || error "$report_path not yaml formatted"
+
+       no_of_files=$(awk '/Count:/ {print $2; exit}' "$report_path")
+       total_size=$(awk '/Total:/ {print $2; exit}' "$report_path")
+
+       (( no_of_files == 1 )) ||
+               error "expected no_of_files value 1, got '$no_of_files'"
+
+       [[ "$total_size" != "10240" ]] &&
+               error "expected total_size value 10240, got '$total_size'"
+
+       echo "--collect-fsize-stats works"
+}
+run_test 306 "lipe_scan3 --collect-fsize-stats works correctly"
+
 # loading and scripts
 
 test_400() {