--- /dev/null
+/*
+ * 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(¤t_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(¤t_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);
+}