From cdf9dbd37eb17155a7f4e6c77fcdb44fa1ca7ea9 Mon Sep 17 00:00:00 2001
From: Danny Auble <da@llnl.gov>
Date: Thu, 2 Dec 2010 00:38:27 +0000
Subject: [PATCH] added new report for sreport job SizesByAccountAndWckey

---
 contribs/perlapi/libslurmdb/perl/cluster.c |   5 +-
 doc/man/man1/sreport.1                     |  16 +-
 slurm/slurmdb.h                            |   7 +
 src/db_api/job_report_functions.c          | 315 ++++++++++++++-------
 src/sreport/common.c                       |   4 +-
 src/sreport/job_reports.c                  | 237 +++++++++++++++-
 src/sreport/job_reports.h                  |   1 +
 src/sreport/sreport.c                      |  22 +-
 8 files changed, 488 insertions(+), 119 deletions(-)

diff --git a/contribs/perlapi/libslurmdb/perl/cluster.c b/contribs/perlapi/libslurmdb/perl/cluster.c
index 7757957cf31..f94e9492032 100644
--- a/contribs/perlapi/libslurmdb/perl/cluster.c
+++ b/contribs/perlapi/libslurmdb/perl/cluster.c
@@ -218,7 +218,8 @@ hv_to_user_cond(HV* hv, slurmdb_user_cond_t* user_cond)
 int
 report_job_grouping_to_hv(slurmdb_report_job_grouping_t* rec, HV* hv)
 {
-    /* List jobs should be null*/
+    /* FIX ME: include the job list here (is is not NULL, as
+     * previously thought) */
     STORE_FIELD(hv, rec, min_size, uint32_t);
     STORE_FIELD(hv, rec, max_size, uint32_t);
     STORE_FIELD(hv, rec, count,    uint32_t);
@@ -236,6 +237,7 @@ report_acct_grouping_to_hv(slurmdb_report_acct_grouping_t* rec, HV* hv)
     ListIterator itr = NULL;
 
     STORE_FIELD(hv, rec, acct,     charp);
+    STORE_FIELD(hv, rec, count,    uint32_t);
     STORE_FIELD(hv, rec, cpu_secs, uint64_t);
     STORE_FIELD(hv, rec, lft,      uint32_t);
     STORE_FIELD(hv, rec, rgt,      uint32_t);
@@ -266,6 +268,7 @@ report_cluster_grouping_to_hv(slurmdb_report_cluster_grouping_t* rec, HV* hv)
     ListIterator itr = NULL;
 
     STORE_FIELD(hv, rec, cluster,  charp);
+    STORE_FIELD(hv, rec, count,    uint32_t);
     STORE_FIELD(hv, rec, cpu_secs, uint64_t);
 
     if (rec->acct_list) {
diff --git a/doc/man/man1/sreport.1 b/doc/man/man1/sreport.1
index c326f23959f..aa0ba34b6c1 100644
--- a/doc/man/man1/sreport.1
+++ b/doc/man/man1/sreport.1
@@ -140,7 +140,7 @@ Valid report types are:
 UserUtilizationByWckey, Utilization, WCKeyUtilizationByUser
 
 .B job
-\- SizesByAccount, SizesByWckey
+\- SizesByAccount, SizesByAccountAndWcKey, SizesByWckey
 
 .B reservation
 \- Utilization
@@ -188,6 +188,12 @@ accounts with the 'account=' option sreport will use those accounts as
 the root account and you will receive the sub accounts for the
 accounts listed.
 .TP
+.B job SizesByAccountAndWckey
+This report is very similar to SizesByAccount with the difference being
+each account is pair with wckeys so the identifier is account:wckey
+instead of just account so there will most likely be multiple accounts
+listed depending on the number of wckeys used.
+.TP
 .B job SizesByWckey
 This report will dispay the amount of time for each wckey for job ranges
 specified by the 'grouping=' option.
@@ -277,15 +283,15 @@ List of wckeys to include in report. Default is all.
 
 .TP
 .B Accounts=<OPT>
-List of accounts to use for the report Default is all.  The SizesByAccount
+List of accounts to use for the report Default is all.  The SizesByAccount(*)
 report only displays 1 hierarchical level. If accounts are specified
 the next layer of accounts under those specified will be displayed,
-not the accounts specified.  In the SizesByAccount reports the default
+not the accounts specified.  In the SizesByAccount(*) reports the default
 for accounts is root.  This explanation does not apply when ran with
 the FlatView option.
 .TP
 .B FlatView
-When used with the SizesbyAccount will not group accounts in a
+When used with the SizesbyAccount(*) will not group accounts in a
 hierarchical level, but print each account where jobs ran on a
 separate line without any hierarchy.
 .TP
@@ -371,7 +377,7 @@ Allocated, Cluster, CPUCount, Down, Idle, Overcommited, PlannedDown, Reported, R
 .TP
 \fBFORMAT OPTIONS FOR JOB REPORTS\fP
 .in 10
-SizesByAccount:
+SizesByAccount, SizesByAccountAndWckey:
 .in 14
 Account, Cluster
 
diff --git a/slurm/slurmdb.h b/slurm/slurmdb.h
index 9c8c9893e21..61a2c17833f 100644
--- a/slurm/slurmdb.h
+++ b/slurm/slurmdb.h
@@ -851,6 +851,7 @@ typedef struct {
 
 typedef struct {
 	char *acct; /*account name */
+	uint32_t count; /* total count of jobs taken up by this acct */
 	uint64_t cpu_secs; /* how many cpus secs taken up by this
 			    * acct */
 	List groups; /* containing slurmdb_report_job_grouping_t's*/
@@ -860,6 +861,7 @@ typedef struct {
 
 typedef struct {
 	char *cluster; /*cluster name */
+	uint32_t count; /* total count of jobs taken up by this cluster */
 	uint64_t cpu_secs; /* how many cpus secs taken up by this
 			    * cluster */
 	List acct_list; /* containing slurmdb_report_acct_grouping_t's */
@@ -1041,6 +1043,11 @@ extern List slurmdb_report_job_sizes_grouped_by_top_account(void *db_conn,
 extern List slurmdb_report_job_sizes_grouped_by_wckey(void *db_conn,
 	slurmdb_job_cond_t *job_cond, List grouping_list);
 
+extern List slurmdb_report_job_sizes_grouped_by_top_account_then_wckey(
+	void *db_conn, slurmdb_job_cond_t *job_cond,
+	List grouping_list, bool flat_view);
+
+
 /* report on users with top usage
  * IN: slurmdb_user_cond_t *user_cond
  * IN: group_accounts - Whether or not to group all accounts together
diff --git a/src/db_api/job_report_functions.c b/src/db_api/job_report_functions.c
index 647c5c0345c..8d702571cab 100644
--- a/src/db_api/job_report_functions.c
+++ b/src/db_api/job_report_functions.c
@@ -62,20 +62,99 @@ static int _sort_group_asc(char *group_a, char *group_b)
 	return 0;
 }
 
-static List _process_grouped_report(void *db_conn,
-	slurmdb_job_cond_t *job_cond, List grouping_list,
-	bool flat_view, bool wckey_type)
+static void _check_create_grouping(
+	List cluster_list,  ListIterator group_itr,
+	char *cluster, char *name, void *object,
+	bool individual, bool wckey_type)
+{
+	ListIterator itr;
+	slurmdb_wckey_rec_t *wckey = (slurmdb_wckey_rec_t *)object;
+	slurmdb_association_rec_t *assoc = (slurmdb_association_rec_t *)object;
+	slurmdb_report_cluster_grouping_t *cluster_group = NULL;
+	slurmdb_report_acct_grouping_t *acct_group = NULL;
+	slurmdb_report_job_grouping_t *job_group = NULL;
+
+	itr = list_iterator_create(cluster_list);
+	while((cluster_group = list_next(itr))) {
+		if(!strcmp(cluster, cluster_group->cluster))
+			break;
+	}
+	list_iterator_destroy(itr);
+
+	if(!cluster_group) {
+		cluster_group = xmalloc(
+			sizeof(slurmdb_report_cluster_grouping_t));
+		cluster_group->cluster = xstrdup(cluster);
+		cluster_group->acct_list = list_create(
+			slurmdb_destroy_report_acct_grouping);
+		list_append(cluster_list, cluster_group);
+	}
+
+	itr = list_iterator_create(cluster_group->acct_list);
+	while((acct_group = list_next(itr))) {
+		if(!strcmp(name, acct_group->acct))
+			break;
+	}
+	list_iterator_destroy(itr);
+
+	if(!acct_group) {
+		uint32_t last_size = 0;
+		char *group = NULL;
+		acct_group = xmalloc(sizeof(slurmdb_report_acct_grouping_t));
+
+		acct_group->acct = xstrdup(name);
+		if (wckey_type == 1 || wckey_type == 3)
+			acct_group->lft = wckey->id;
+		else {
+			acct_group->lft = assoc->lft;
+			acct_group->rgt = assoc->rgt;
+		}
+		acct_group->groups = list_create(
+			slurmdb_destroy_report_job_grouping);
+		list_append(cluster_group->acct_list, acct_group);
+		while ((group = list_next(group_itr))) {
+			job_group = xmalloc(
+				sizeof(slurmdb_report_job_grouping_t));
+			job_group->jobs = list_create(NULL);
+			if(!individual)
+				job_group->min_size = last_size;
+			last_size = atoi(group);
+			if(!individual)
+				job_group->max_size = last_size-1;
+			else
+				job_group->min_size =
+					job_group->max_size = last_size;
+			list_append(acct_group->groups, job_group);
+		}
+		if(last_size && !individual) {
+			job_group = xmalloc(
+				sizeof(slurmdb_report_job_grouping_t));
+			job_group->jobs = list_create(NULL);
+			job_group->min_size = last_size;
+			if(individual)
+				job_group->max_size =
+					job_group->min_size;
+			else
+				job_group->max_size = INFINITE;
+			list_append(acct_group->groups, job_group);
+		}
+		list_iterator_reset(group_itr);
+	}
+}
+
+static List _process_grouped_report(
+	void *db_conn, slurmdb_job_cond_t *job_cond, List grouping_list,
+	bool flat_view, bool wckey_type, bool both)
 {
 	int exit_code = 0;
-	void *object = NULL;
+	void *object = NULL, *object2 = NULL;
 
-	ListIterator itr = NULL;
+	ListIterator itr = NULL, itr2 = NULL;
 	ListIterator cluster_itr = NULL;
 	ListIterator local_itr = NULL;
 	ListIterator acct_itr = NULL;
 	ListIterator group_itr = NULL;
 
-
 	slurmdb_job_rec_t *job = NULL;
 	slurmdb_report_cluster_grouping_t *cluster_group = NULL;
 	slurmdb_report_acct_grouping_t *acct_group = NULL;
@@ -83,7 +162,7 @@ static List _process_grouped_report(void *db_conn,
 
 	List job_list = NULL;
 	List cluster_list = NULL;
-	List object_list = NULL;
+	List object_list = NULL, object2_list = NULL;
 
 	List tmp_acct_list = NULL;
 	bool destroy_job_cond = 0;
@@ -152,15 +231,7 @@ static List _process_grouped_report(void *db_conn,
 	if(flat_view)
 		goto no_objects;
 
-	if(wckey_type) {
-		slurmdb_wckey_cond_t wckey_cond;
-		memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t));
-		wckey_cond.name_list = job_cond->wckey_list;
-		wckey_cond.cluster_list = job_cond->cluster_list;
-
-		object_list = acct_storage_g_get_wckeys(db_conn, my_uid,
-							&wckey_cond);
-	} else {
+	if (!wckey_type || both) {
 		slurmdb_association_cond_t assoc_cond;
 		memset(&assoc_cond, 0, sizeof(slurmdb_association_cond_t));
 		assoc_cond.id_list = job_cond->associd_list;
@@ -178,98 +249,83 @@ static List _process_grouped_report(void *db_conn,
 							      &assoc_cond);
 	}
 
+	if (wckey_type || both) {
+		slurmdb_wckey_cond_t wckey_cond;
+		memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t));
+		wckey_cond.name_list = job_cond->wckey_list;
+		wckey_cond.cluster_list = job_cond->cluster_list;
+
+		object2_list = acct_storage_g_get_wckeys(db_conn, my_uid,
+							 &wckey_cond);
+		if (!object_list) {
+			object_list = object2_list;
+			object2_list = NULL;
+		}
+	}
+
 	if(!object_list) {
 		debug2(" No join list given.\n");
 		goto no_objects;
 	}
 
 	itr = list_iterator_create(object_list);
+	if (object2_list)
+		itr2 = list_iterator_create(object2_list);
 	while((object = list_next(itr))) {
-		char *cluster = NULL, *name = NULL;
+		char *cluster = NULL;
 		slurmdb_wckey_rec_t *wckey = (slurmdb_wckey_rec_t *)object;
 		slurmdb_association_rec_t *assoc =
 			(slurmdb_association_rec_t *)object;
-
-		if(wckey_type) {
-			cluster = wckey->cluster;
-			name = wckey->name;
-		} else {
-			cluster = assoc->cluster;
-			name = assoc->acct;
-		}
-
-		while((cluster_group = list_next(cluster_itr))) {
-			if(!strcmp(cluster, cluster_group->cluster))
-				break;
-		}
-		if(!cluster_group) {
-			cluster_group = xmalloc(
-				sizeof(slurmdb_report_cluster_grouping_t));
-			cluster_group->cluster = xstrdup(cluster);
-			cluster_group->acct_list = list_create(
-				slurmdb_destroy_report_acct_grouping);
-			list_append(cluster_list, cluster_group);
-		}
-
-		acct_itr = list_iterator_create(cluster_group->acct_list);
-		while((acct_group = list_next(acct_itr))) {
-			if(!strcmp(name, acct_group->acct))
-				break;
+		if (!itr2) {
+			char *name = NULL;
+			if(wckey_type) {
+				cluster = wckey->cluster;
+				name = wckey->name;
+			} else {
+				cluster = assoc->cluster;
+				name = assoc->acct;
+			}
+			_check_create_grouping(cluster_list, group_itr,
+					       cluster, name, object,
+					       individual, wckey_type);
+			continue;
 		}
-		list_iterator_destroy(acct_itr);
 
-		if(!acct_group) {
-			uint32_t last_size = 0;
-			char *group = NULL;
-			acct_group = xmalloc(
-				sizeof(slurmdb_report_acct_grouping_t));
-			acct_group->acct = xstrdup(name);
-			if(wckey_type)
-				acct_group->lft = wckey->id;
-			else {
-				acct_group->lft = assoc->lft;
-				acct_group->rgt = assoc->rgt;
-			}
-			acct_group->groups = list_create(
-				slurmdb_destroy_report_job_grouping);
-			list_append(cluster_group->acct_list, acct_group);
-			while((group = list_next(group_itr))) {
-				job_group = xmalloc(
-					sizeof(slurmdb_report_job_grouping_t));
-				job_group->jobs = list_create(NULL);
-				if(!individual)
-					job_group->min_size = last_size;
-				last_size = atoi(group);
-				if(!individual)
-					job_group->max_size = last_size-1;
-				else
-					job_group->min_size =
-						job_group->max_size = last_size;
-				list_append(acct_group->groups, job_group);
+		while((object2 = list_next(itr2))) {
+			slurmdb_wckey_rec_t *wckey2 =
+				(slurmdb_wckey_rec_t *)object2;
+			slurmdb_association_rec_t *assoc2 =
+				(slurmdb_association_rec_t *)object2;
+			char name[200];
+			if (!wckey_type) {
+				if (strcmp(assoc->cluster, wckey2->cluster))
+					continue;
+				cluster = assoc->cluster;
+				snprintf(name, sizeof(name), "%s:%s",
+					 assoc->acct, wckey2->name);
+			} else {
+				if (strcmp(wckey->cluster, assoc2->cluster))
+					continue;
+				cluster = wckey->cluster;
+				snprintf(name, sizeof(name), "%s:%s",
+					 wckey2->name, assoc->acct);
 			}
-			if(last_size && !individual) {
-				job_group = xmalloc(
-					sizeof(slurmdb_report_job_grouping_t));
-				job_group->jobs = list_create(NULL);
-				job_group->min_size = last_size;
-				if(individual)
-					job_group->max_size =
-						job_group->min_size;
-				else
-					job_group->max_size = INFINITE;
-				list_append(acct_group->groups, job_group);
-			}
-			list_iterator_reset(group_itr);
+			_check_create_grouping(cluster_list, group_itr,
+					       cluster, name, object,
+					       individual, wckey_type);
 		}
-		list_iterator_reset(cluster_itr);
+		list_iterator_reset(itr2);
 	}
 	list_iterator_destroy(itr);
+	if (itr2)
+		list_iterator_destroy(itr2);
+
 no_objects:
 	itr = list_iterator_create(job_list);
 
 	while((job = list_next(itr))) {
 		char *local_cluster = "UNKNOWN";
-		char *local_account = "UNKNOWN";
+		char tmp_acct[200];
 
 		if(!job->elapsed) {
 			/* here we don't care about jobs that didn't
@@ -278,8 +334,28 @@ no_objects:
 		}
 		if(job->cluster)
 			local_cluster = job->cluster;
-		if(job->account)
-			local_account = job->account;
+
+		if (!wckey_type) {
+			if (both && job->wckey) {
+				snprintf(tmp_acct, sizeof(tmp_acct),
+					 "%s:%s",
+					 job->account,
+					 job->wckey);
+			} else {
+				snprintf(tmp_acct, sizeof(tmp_acct),
+					 "%s", job->account);
+			}
+		} else {
+			if (both && job->account) {
+				snprintf(tmp_acct, sizeof(tmp_acct),
+					 "%s:%s",
+					 job->wckey,
+					 job->account);
+			} else {
+				snprintf(tmp_acct, sizeof(tmp_acct),
+					 "%s", job->wckey);
+			}
+		}
 
 		list_iterator_reset(cluster_itr);
 		while((cluster_group = list_next(cluster_itr))) {
@@ -302,8 +378,8 @@ no_objects:
 
 		acct_itr = list_iterator_create(cluster_group->acct_list);
 		while((acct_group = list_next(acct_itr))) {
-			if(wckey_type) {
-				if(!strcmp(job->wckey, acct_group->acct))
+			if (wckey_type) {
+				if(!strcmp(tmp_acct, acct_group->acct))
 					break;
 				continue;
 			}
@@ -314,10 +390,21 @@ no_objects:
 				/* keep separate since we don't want
 				 * to so a strcmp if we don't have to
 				 */
-				if(job->lft > acct_group->lft
-				   && job->lft < acct_group->rgt)
-					break;
-			} else if(!strcmp(acct_group->acct, local_account))
+				if (job->lft > acct_group->lft
+				    && job->lft < acct_group->rgt) {
+					char *mywckey;
+					if (!both)
+						break;
+					mywckey = strstr(acct_group->acct, ":");
+					mywckey++;
+					if (!job->wckey && !mywckey)
+						break;
+					else if (!mywckey || !job->wckey)
+						continue;
+					else if (!strcmp(mywckey, job->wckey))
+						break;
+				}
+			} else if (!strcmp(acct_group->acct, tmp_acct))
 				break;
 		}
 		list_iterator_destroy(acct_itr);
@@ -333,7 +420,7 @@ no_objects:
 
 			acct_group = xmalloc(
 				sizeof(slurmdb_report_acct_grouping_t));
-			acct_group->acct = xstrdup(local_account);
+			acct_group->acct = xstrdup(tmp_acct);
 			acct_group->groups = list_create(
 				slurmdb_destroy_report_job_grouping);
 			list_append(cluster_group->acct_list, acct_group);
@@ -375,6 +462,8 @@ no_objects:
 				continue;
 			list_append(job_group->jobs, job);
 			job_group->count++;
+			acct_group->count++;
+			cluster_group->count++;
 			total_secs = (uint64_t)job->elapsed
 				* (uint64_t)job->alloc_cpus;
 			job_group->cpu_secs += total_secs;
@@ -385,12 +474,31 @@ no_objects:
 	}
 	list_iterator_destroy(itr);
 	list_iterator_destroy(group_itr);
+	list_iterator_reset(cluster_itr);
+	while ((cluster_group = list_next(cluster_itr))) {
+		ListIterator acct_itr;
+		if (!cluster_group->count) {
+			list_delete_item(cluster_itr);
+			continue;
+		}
+		acct_itr = list_iterator_create(cluster_group->acct_list);
+		while ((acct_group = list_next(acct_itr))) {
+			if (!acct_group->count) {
+				list_delete_item(acct_itr);
+				continue;
+			}
+		}
+		list_iterator_destroy(acct_itr);
+	}
 	list_iterator_destroy(cluster_itr);
 
 end_it:
 	if(object_list)
 		list_destroy(object_list);
 
+	if(object2_list)
+		list_destroy(object2_list);
+
 	if(destroy_job_cond)
 		slurmdb_destroy_job_cond(job_cond);
 
@@ -407,16 +515,25 @@ end_it:
 	return cluster_list;
 }
 
-
 extern List slurmdb_report_job_sizes_grouped_by_top_account(void *db_conn,
 	slurmdb_job_cond_t *job_cond, List grouping_list, bool flat_view)
 {
 	return _process_grouped_report(db_conn, job_cond, grouping_list,
-				       flat_view, 0);
+				       flat_view, 0, 0);
 }
 
 extern List slurmdb_report_job_sizes_grouped_by_wckey(void *db_conn,
 	slurmdb_job_cond_t *job_cond, List grouping_list)
 {
-	return _process_grouped_report(db_conn, job_cond, grouping_list, 0, 1);
+	return _process_grouped_report(db_conn, job_cond, grouping_list,
+				       0, 1, 0);
 }
+
+extern List slurmdb_report_job_sizes_grouped_by_top_account_then_wckey(
+	void *db_conn, slurmdb_job_cond_t *job_cond,
+	List grouping_list, bool flat_view)
+{
+	return _process_grouped_report(
+		db_conn, job_cond, grouping_list, flat_view, 0, 1);
+}
+
diff --git a/src/sreport/common.c b/src/sreport/common.c
index f61eee6e86a..88f9ac2dfc3 100644
--- a/src/sreport/common.c
+++ b/src/sreport/common.c
@@ -40,8 +40,8 @@
 
 #include "sreport.h"
 
-extern void slurmdb_report_print_time(print_field_t *field,
-			       uint64_t value, uint64_t total_time, int last)
+extern void slurmdb_report_print_time(print_field_t *field, uint64_t value,
+				      uint64_t total_time, int last)
 {
 	int abs_len = abs(field->len);
 
diff --git a/src/sreport/job_reports.c b/src/sreport/job_reports.c
index a448db5c1c0..45c00483f90 100644
--- a/src/sreport/job_reports.c
+++ b/src/sreport/job_reports.c
@@ -578,6 +578,8 @@ extern int job_sizes_grouped_by_top_acct(int argc, char *argv[])
 
 	int i=0;
 
+	uint64_t count1, count2;
+
 	ListIterator itr = NULL;
 	ListIterator itr2 = NULL;
 	ListIterator cluster_itr = NULL;
@@ -724,9 +726,15 @@ extern int job_sizes_grouped_by_top_acct(int argc, char *argv[])
 
 			temp_format = time_format;
 			time_format = SLURMDB_REPORT_TIME_PERCENT;
+			if (!print_job_count) {
+				count1 = acct_group->cpu_secs;
+				count2 = cluster_group->cpu_secs;
+			} else {
+				count1 = acct_group->count;
+				count2 = cluster_group->count;
+			}
 			total_field.print_routine(&total_field,
-						  acct_group->cpu_secs,
-						  cluster_group->cpu_secs, 1);
+						  count1, count2, 1);
 			time_format = temp_format;
 			printf("\n");
 		}
@@ -737,6 +745,7 @@ extern int job_sizes_grouped_by_top_acct(int argc, char *argv[])
 //	time_format = temp_time_format;
 
 end_it:
+	xfree(total_field.name);
 	if(print_job_count)
 		print_job_count = 0;
 
@@ -779,6 +788,8 @@ extern int job_sizes_grouped_by_wckey(int argc, char *argv[])
 	slurmdb_job_cond_t *job_cond = xmalloc(sizeof(slurmdb_job_cond_t));
 	int i=0;
 
+	uint64_t count1, count2;
+
 	ListIterator itr = NULL;
 	ListIterator itr2 = NULL;
 	ListIterator cluster_itr = NULL;
@@ -925,9 +936,15 @@ extern int job_sizes_grouped_by_wckey(int argc, char *argv[])
 
 			temp_format = time_format;
 			time_format = SLURMDB_REPORT_TIME_PERCENT;
+			if (!print_job_count) {
+				count1 = acct_group->cpu_secs;
+				count2 = cluster_group->cpu_secs;
+			} else {
+				count1 = acct_group->count;
+				count2 = cluster_group->count;
+			}
 			total_field.print_routine(&total_field,
-						  acct_group->cpu_secs,
-						  cluster_group->cpu_secs, 1);
+						  count1, count2, 1);
 			time_format = temp_format;
 			printf("\n");
 		}
@@ -938,6 +955,7 @@ extern int job_sizes_grouped_by_wckey(int argc, char *argv[])
 //	time_format = temp_time_format;
 
 end_it:
+	xfree(total_field.name);
 	if(print_job_count)
 		print_job_count = 0;
 
@@ -974,3 +992,214 @@ end_it:
 	return rc;
 }
 
+extern int job_sizes_grouped_by_top_acct_and_wckey(int argc, char *argv[])
+{
+	int rc = SLURM_SUCCESS;
+	slurmdb_job_cond_t *job_cond = xmalloc(sizeof(slurmdb_job_cond_t));
+
+	int i=0;
+
+	uint64_t count1, count2;
+
+	ListIterator itr = NULL;
+	ListIterator itr2 = NULL;
+	ListIterator cluster_itr = NULL;
+	ListIterator local_itr = NULL;
+	ListIterator acct_itr = NULL;
+
+	slurmdb_report_cluster_grouping_t *cluster_group = NULL;
+	slurmdb_report_acct_grouping_t *acct_group = NULL;
+	slurmdb_report_job_grouping_t *job_group = NULL;
+
+	print_field_t *field = NULL;
+	print_field_t total_field;
+	uint32_t total_time = 0;
+	slurmdb_report_time_format_t temp_format;
+
+	List slurmdb_report_cluster_grouping_list = NULL;
+	List assoc_list = NULL;
+
+	List format_list = list_create(slurm_destroy_char);
+	List grouping_list = list_create(slurm_destroy_char);
+
+	List header_list = NULL;
+
+//	slurmdb_report_time_format_t temp_time_format = time_format;
+
+	print_fields_list = list_create(destroy_print_field);
+
+	_set_cond(&i, argc, argv, job_cond, format_list, grouping_list);
+
+	if(!list_count(format_list))
+		slurm_addto_char_list(format_list, "Cl,a%-20");
+
+	if(!individual_grouping && !list_count(grouping_list))
+		slurm_addto_char_list(grouping_list, "50,250,500,1000");
+
+	_setup_print_fields_list(format_list);
+	list_destroy(format_list);
+
+	if(!(slurmdb_report_cluster_grouping_list =
+	     slurmdb_report_job_sizes_grouped_by_top_account_then_wckey(
+		     db_conn, job_cond, grouping_list, flat_view))) {
+		exit_code = 1;
+		goto end_it;
+	}
+
+	_setup_grouping_print_fields_list(grouping_list);
+
+	if(print_fields_have_header) {
+		char start_char[20];
+		char end_char[20];
+		time_t my_start = job_cond->usage_start;
+		time_t my_end = job_cond->usage_end-1;
+
+		slurm_make_time_str(&my_start, start_char, sizeof(start_char));
+		slurm_make_time_str(&my_end, end_char, sizeof(end_char));
+		printf("----------------------------------------"
+		       "----------------------------------------\n");
+		printf("Job Sizes %s - %s (%d secs)\n",
+		       start_char, end_char,
+		       (int)(job_cond->usage_end - job_cond->usage_start));
+		if(print_job_count)
+			printf("Units are in number of jobs ran\n");
+		else
+			printf("Time reported in %s\n", time_format_string);
+		printf("----------------------------------------"
+		       "----------------------------------------\n");
+	}
+	total_time = job_cond->usage_end - job_cond->usage_start;
+
+	header_list = list_create(NULL);
+	list_append_list(header_list, print_fields_list);
+	list_append_list(header_list, grouping_print_fields_list);
+
+	memset(&total_field, 0, sizeof(print_field_t));
+	total_field.type = PRINT_JOB_SIZE;
+	total_field.name = xstrdup("% of cluster");
+	total_field.len = 12;
+	total_field.print_routine = slurmdb_report_print_time;
+	list_append(header_list, &total_field);
+
+	print_fields_header(header_list);
+	list_destroy(header_list);
+
+//	time_format = SLURMDB_REPORT_TIME_PERCENT;
+
+	itr = list_iterator_create(print_fields_list);
+	itr2 = list_iterator_create(grouping_print_fields_list);
+	list_sort(slurmdb_report_cluster_grouping_list,
+		  (ListCmpF)_sort_cluster_grouping_dec);
+	cluster_itr =
+		list_iterator_create(slurmdb_report_cluster_grouping_list);
+	while((cluster_group = list_next(cluster_itr))) {
+		list_sort(cluster_group->acct_list,
+			  (ListCmpF)_sort_acct_grouping_dec);
+		acct_itr = list_iterator_create(cluster_group->acct_list);
+		while((acct_group = list_next(acct_itr))) {
+
+			while((field = list_next(itr))) {
+				switch(field->type) {
+				case PRINT_JOB_CLUSTER:
+					field->print_routine(
+						field,
+						cluster_group->cluster, 0);
+					break;
+				case PRINT_JOB_ACCOUNT:
+					field->print_routine(field,
+							     acct_group->acct,
+							     0);
+					break;
+				default:
+					field->print_routine(field,
+							     NULL,
+							     0);
+					break;
+				}
+			}
+			list_iterator_reset(itr);
+			local_itr = list_iterator_create(acct_group->groups);
+			while((job_group = list_next(local_itr))) {
+				field = list_next(itr2);
+				switch(field->type) {
+				case PRINT_JOB_SIZE:
+					field->print_routine(
+						field,
+						job_group->cpu_secs,
+						acct_group->cpu_secs,
+						0);
+					break;
+				case PRINT_JOB_COUNT:
+					field->print_routine(
+						field,
+						job_group->count,
+						0);
+					break;
+				default:
+					field->print_routine(field,
+							     NULL,
+							     0);
+					break;
+				}
+			}
+			list_iterator_reset(itr2);
+			list_iterator_destroy(local_itr);
+
+			temp_format = time_format;
+			time_format = SLURMDB_REPORT_TIME_PERCENT;
+			if (!print_job_count) {
+				count1 = acct_group->cpu_secs;
+				count2 = cluster_group->cpu_secs;
+			} else {
+				count1 = acct_group->count;
+				count2 = cluster_group->count;
+			}
+			total_field.print_routine(&total_field,
+						  count1, count2, 1);
+			time_format = temp_format;
+			printf("\n");
+		}
+		list_iterator_destroy(acct_itr);
+	}
+	list_iterator_destroy(itr);
+
+//	time_format = temp_time_format;
+
+end_it:
+	xfree(total_field.name);
+	if(print_job_count)
+		print_job_count = 0;
+
+	if(individual_grouping)
+		individual_grouping = 0;
+
+	slurmdb_destroy_job_cond(job_cond);
+
+	if(grouping_list) {
+		list_destroy(grouping_list);
+		grouping_list = NULL;
+	}
+
+	if(assoc_list) {
+		list_destroy(assoc_list);
+		assoc_list = NULL;
+	}
+
+	if(slurmdb_report_cluster_grouping_list) {
+		list_destroy(slurmdb_report_cluster_grouping_list);
+		slurmdb_report_cluster_grouping_list = NULL;
+	}
+
+	if(print_fields_list) {
+		list_destroy(print_fields_list);
+		print_fields_list = NULL;
+	}
+
+	if(grouping_print_fields_list) {
+		list_destroy(grouping_print_fields_list);
+		grouping_print_fields_list = NULL;
+	}
+
+	return rc;
+}
+
diff --git a/src/sreport/job_reports.h b/src/sreport/job_reports.h
index 672d1b6bd6b..c82936a03aa 100644
--- a/src/sreport/job_reports.h
+++ b/src/sreport/job_reports.h
@@ -45,5 +45,6 @@
 
 extern int job_sizes_grouped_by_top_acct(int argc, char *argv[]);
 extern int job_sizes_grouped_by_wckey(int argc, char *argv[]);
+extern int job_sizes_grouped_by_top_acct_and_wckey(int argc, char *argv[]);
 
 #endif
diff --git a/src/sreport/sreport.c b/src/sreport/sreport.c
index 463c206e3c5..db623e3738c 100644
--- a/src/sreport/sreport.c
+++ b/src/sreport/sreport.c
@@ -246,11 +246,17 @@ static void _job_rep (int argc, char *argv[])
 				 "SizesByWcKey", MAX(command_len, 8))) {
 		error_code = job_sizes_grouped_by_wckey(
 			(argc - 1), &argv[1]);
+	} else if (!strncasecmp (argv[0],
+				"SizesByAccountAndWcKey",
+				MAX(command_len, 15))) {
+		error_code = job_sizes_grouped_by_top_acct_and_wckey(
+			(argc - 1), &argv[1]);
 	} else {
 		exit_code = 1;
 		fprintf(stderr, "Not valid report %s\n", argv[0]);
 		fprintf(stderr, "Valid job reports are, ");
-		fprintf(stderr, "\"SizesByAccount, and SizesByWckey\"\n");
+		fprintf(stderr, "\"SizesByAccount, SizesByAccountAndWcKey, ");
+		fprintf(stderr, "and  SizesByWckey\"\n");
 	}
 
 	if (error_code) {
@@ -692,7 +698,7 @@ sreport [<OPTION>] [<COMMAND>]                                             \n\
   <REPORT> is different for each report type.                              \n\
      cluster - AccountUtilizationByUser, UserUtilizationByAccount,         \n\
                UserUtilizationByWckey, Utilization, WCKeyUtilizationByUser \n\
-     job     - SizesByAccount, SizesByWckey                                \n\
+     job     - SizesByAccount, SizesByAccountAndWckey, SizesByWckey        \n\
      reservation                                                           \n\
              - Utilization                                                 \n\
      user    - TopUsage                                                    \n\
@@ -725,16 +731,16 @@ sreport [<OPTION>] [<COMMAND>]                                             \n\
                                   to include in report.  Default is all.   \n\
                                                                            \n\
      job     - Accounts=<OPT>   - List of accounts to use for the report   \n\
-                                  Default is all.  The SizesbyAccount      \n\
+                                  Default is all.  The SizesbyAccount(*)   \n\
                                   report only displays 1 hierarchical level.\n\
                                   If accounts are specified the next layer \n\
                                   of accounts under those specified will be\n\
                                   displayed, not the accounts specified.   \n\
-                                  In the SizesByAccount reports the default\n\
-                                  for accounts is root.  This explanation  \n\
-                                  does not apply when ran with the FlatView\n\
-                                  option.                                  \n\
-             - FlatView         - When used with the SizesbyAccount        \n\
+                                  In the SizesByAccount(*) reports the     \n\
+                                  default for accounts is root.  This      \n\
+                                  explanation does not apply when ran with \n\
+                                  the FlatView option.                     \n\
+             - FlatView         - When used with the SizesbyAccount(*)     \n\
                                   will not group accounts in a             \n\
                                   hierarchical level, but print each       \n\
                                   account where jobs ran on a separate     \n\
-- 
GitLab