1 /*****************************************************************************\
2  *  cluster_report_functions.c - Interface to functions dealing with cluster
3  *                               reports.
4  ******************************************************************************
5  *  Copyright (C) 2010 Lawrence Livermore National Security.
6  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7  *  Written by Danny Auble da@llnl.gov, et. al.
8  *  CODE-OCEC-09-009. All rights reserved.
9  *
10  *  This file is part of Slurm, a resource management program.
11  *  For details, see <https://slurm.schedmd.com/>.
12  *  Please also read the included file: DISCLAIMER.
13  *
14  *  Slurm is free software; you can redistribute it and/or modify it under
15  *  the terms of the GNU General Public License as published by the Free
16  *  Software Foundation; either version 2 of the License, or (at your option)
17  *  any later version.
18  *
19  *  In addition, as a special exception, the copyright holders give permission
20  *  to link the code of portions of this program with the OpenSSL library under
21  *  certain conditions as described in each individual source file, and
22  *  distribute linked combinations including the two. You must obey the GNU
23  *  General Public License in all respects for all of the code used other than
24  *  OpenSSL. If you modify file(s) with this exception, you may extend this
25  *  exception to your version of the file(s), but you are not obligated to do
26  *  so. If you do not wish to do so, delete this exception statement from your
27  *  version.  If you delete this exception statement from all source files in
28  *  the program, then also delete it here.
29  *
30  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
31  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
32  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
33  *  details.
34  *
35  *  You should have received a copy of the GNU General Public License along
36  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
37  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
38 \*****************************************************************************/
39 
40 #include "slurm/slurm.h"
41 #include "slurm/slurm_errno.h"
42 
43 #include "src/common/slurmdb_defs.h"
44 #include "src/common/slurm_accounting_storage.h"
45 #include "src/common/xstring.h"
46 
47 typedef enum {
48 	CLUSTER_REPORT_UA,
49 	CLUSTER_REPORT_AU,
50 	CLUSTER_REPORT_UW,
51 	CLUSTER_REPORT_WU
52 } cluster_report_t;
53 
_process_ua(List user_list,slurmdb_assoc_rec_t * assoc)54 static void _process_ua(List user_list, slurmdb_assoc_rec_t *assoc)
55 {
56 	ListIterator itr = NULL;
57 	slurmdb_report_user_rec_t *slurmdb_report_user = NULL;
58 
59 	/* make sure we add all associations to this
60 	   user rec because we could have some in
61 	   partitions which would create another
62 	   record otherwise
63 	*/
64 	itr = list_iterator_create(user_list);
65 	while((slurmdb_report_user = list_next(itr))) {
66 		if (!xstrcmp(slurmdb_report_user->name, assoc->user)
67 		   && !xstrcmp(slurmdb_report_user->acct, assoc->acct))
68 			break;
69 	}
70 	list_iterator_destroy(itr);
71 
72 	if (!slurmdb_report_user) {
73 		struct passwd *passwd_ptr = NULL;
74 		uid_t uid = NO_VAL;
75 		passwd_ptr = getpwnam(assoc->user);
76 		if (passwd_ptr)
77 			uid = passwd_ptr->pw_uid;
78 		/* In this report we are using the slurmdb_report user
79 		   structure to store the information we want
80 		   since it is already available and will do
81 		   pretty much what we want.
82 		*/
83 		slurmdb_report_user =
84 			xmalloc(sizeof(slurmdb_report_user_rec_t));
85 		slurmdb_report_user->name = xstrdup(assoc->user);
86 		slurmdb_report_user->uid = uid;
87 		slurmdb_report_user->acct = xstrdup(assoc->acct);
88 
89 		list_append(user_list, slurmdb_report_user);
90 	}
91 
92 	/* get the amount of time this assoc used
93 	   during the time we are looking at */
94 	slurmdb_transfer_acct_list_2_tres(assoc->accounting_list,
95 					  &slurmdb_report_user->tres_list);
96 }
97 
_process_au(List assoc_list,slurmdb_assoc_rec_t * assoc)98 static void _process_au(List assoc_list, slurmdb_assoc_rec_t *assoc)
99 {
100 	slurmdb_report_assoc_rec_t *slurmdb_report_assoc =
101 		xmalloc(sizeof(slurmdb_report_assoc_rec_t));
102 
103 	list_append(assoc_list, slurmdb_report_assoc);
104 
105 	slurmdb_report_assoc->acct = xstrdup(assoc->acct);
106 	slurmdb_report_assoc->cluster = xstrdup(assoc->cluster);
107 	slurmdb_report_assoc->parent_acct = xstrdup(assoc->parent_acct);
108 	slurmdb_report_assoc->user = xstrdup(assoc->user);
109 
110 	/* get the amount of time this assoc used
111 	   during the time we are looking at */
112 	slurmdb_transfer_acct_list_2_tres(assoc->accounting_list,
113 					  &slurmdb_report_assoc->tres_list);
114 }
115 
_process_uw(List user_list,slurmdb_wckey_rec_t * wckey)116 static void _process_uw(List user_list, slurmdb_wckey_rec_t *wckey)
117 {
118 	slurmdb_report_user_rec_t *slurmdb_report_user = NULL;
119 	struct passwd *passwd_ptr = NULL;
120 	uid_t uid = NO_VAL;
121 
122 	passwd_ptr = getpwnam(wckey->user);
123 	if (passwd_ptr)
124 		uid = passwd_ptr->pw_uid;
125 	/* In this report we are using the slurmdb_report user
126 	   structure to store the information we want
127 	   since it is already available and will do
128 	   pretty much what we want.
129 	*/
130 	slurmdb_report_user =
131 		xmalloc(sizeof(slurmdb_report_user_rec_t));
132 	slurmdb_report_user->name = xstrdup(wckey->user);
133 	slurmdb_report_user->uid = uid;
134 	slurmdb_report_user->acct = xstrdup(wckey->name);
135 
136 	list_append(user_list, slurmdb_report_user);
137 
138 	/* get the amount of time this wckey used
139 	   during the time we are looking at */
140 	slurmdb_transfer_acct_list_2_tres(wckey->accounting_list,
141 					  &slurmdb_report_user->tres_list);
142 }
143 
_process_wu(List assoc_list,slurmdb_wckey_rec_t * wckey)144 static void _process_wu(List assoc_list, slurmdb_wckey_rec_t *wckey)
145 {
146 	slurmdb_report_assoc_rec_t *slurmdb_report_assoc = NULL,
147 		*parent_assoc = NULL;
148 	ListIterator itr = NULL;
149 
150 	/* find the parent */
151 	itr = list_iterator_create(assoc_list);
152 	while((parent_assoc = list_next(itr))) {
153 		if (!parent_assoc->user
154 		   && !xstrcmp(parent_assoc->acct, wckey->name))
155 			break;
156 	}
157 	list_iterator_destroy(itr);
158 	if (!parent_assoc) {
159 		parent_assoc = xmalloc(sizeof(slurmdb_report_assoc_rec_t));
160 
161 		list_append(assoc_list,
162 			    parent_assoc);
163 		parent_assoc->acct = xstrdup(wckey->name);
164 	}
165 
166 	/* now add one for the user */
167 	slurmdb_report_assoc = xmalloc(sizeof(slurmdb_report_assoc_rec_t));
168 	list_append(assoc_list, slurmdb_report_assoc);
169 
170 	slurmdb_report_assoc->acct = xstrdup(wckey->name);
171 	slurmdb_report_assoc->user = xstrdup(wckey->user);
172 
173 	/* get the amount of time this wckey used
174 	   during the time we are looking at */
175 	slurmdb_transfer_acct_list_2_tres(wckey->accounting_list,
176 					  &slurmdb_report_assoc->tres_list);
177 	slurmdb_transfer_acct_list_2_tres(wckey->accounting_list,
178 					  &parent_assoc->tres_list);
179 }
180 
_process_assoc_type(ListIterator itr,slurmdb_report_cluster_rec_t * slurmdb_report_cluster,char * cluster_name,cluster_report_t type)181 static void _process_assoc_type(
182 	ListIterator itr,
183 	slurmdb_report_cluster_rec_t *slurmdb_report_cluster,
184 	char *cluster_name,
185 	cluster_report_t type)
186 {
187 	slurmdb_assoc_rec_t *assoc = NULL;
188 
189 	/* now add the associations of interest here by user */
190 	while((assoc = list_next(itr))) {
191 		if (!assoc->accounting_list
192 		   || !list_count(assoc->accounting_list)
193 		   || ((type == CLUSTER_REPORT_UA) && !assoc->user)) {
194 			list_delete_item(itr);
195 			continue;
196 		}
197 
198 		if (xstrcmp(cluster_name, assoc->cluster))
199 			continue;
200 
201 		if (type == CLUSTER_REPORT_UA)
202 			_process_ua(slurmdb_report_cluster->user_list,
203 				    assoc);
204 		else if (type == CLUSTER_REPORT_AU)
205 			_process_au(slurmdb_report_cluster->assoc_list,
206 				    assoc);
207 
208 		list_delete_item(itr);
209 	}
210 }
211 
_process_wckey_type(ListIterator itr,slurmdb_report_cluster_rec_t * slurmdb_report_cluster,char * cluster_name,cluster_report_t type)212 static void _process_wckey_type(
213 	ListIterator itr,
214 	slurmdb_report_cluster_rec_t *slurmdb_report_cluster,
215 	char *cluster_name,
216 	cluster_report_t type)
217 {
218 	slurmdb_wckey_rec_t *wckey = NULL;
219 
220 	/* now add the wckeyiations of interest here by user */
221 	while((wckey = list_next(itr))) {
222 		if (!wckey->accounting_list
223 		   || !list_count(wckey->accounting_list)
224 		   || ((type == CLUSTER_REPORT_UW) && !wckey->user)) {
225 			list_delete_item(itr);
226 			continue;
227 		}
228 
229 		if (xstrcmp(cluster_name, wckey->cluster))
230 			continue;
231 
232 		if (type == CLUSTER_REPORT_UW)
233 			_process_uw(slurmdb_report_cluster->user_list,
234 				    wckey);
235 		else if (type == CLUSTER_REPORT_WU)
236 			_process_wu(slurmdb_report_cluster->assoc_list,
237 				    wckey);
238 
239 		list_delete_item(itr);
240 	}
241 }
242 
_process_util_by_report(void * db_conn,char * calling_name,void * cond,cluster_report_t type)243 static List _process_util_by_report(void *db_conn, char *calling_name,
244 				    void *cond, cluster_report_t type)
245 {	ListIterator itr = NULL;
246 	ListIterator type_itr = NULL;
247 	slurmdb_cluster_cond_t cluster_cond;
248 	List type_list = NULL;
249 	List cluster_list = NULL;
250 	List first_list = NULL;
251 	slurmdb_cluster_rec_t *cluster = NULL;
252 	slurmdb_report_cluster_rec_t *slurmdb_report_cluster = NULL;
253 	time_t start_time, end_time;
254 
255 	int exit_code = 0;
256 
257 	uid_t my_uid = getuid();
258 	List ret_list = list_create(slurmdb_destroy_report_cluster_rec);
259 
260 	slurmdb_init_cluster_cond(&cluster_cond, 0);
261 
262 	cluster_cond.with_deleted = 1;
263 	cluster_cond.with_usage = 1;
264 	if ((type == CLUSTER_REPORT_UA) || (type == CLUSTER_REPORT_AU)) {
265 		start_time = ((slurmdb_assoc_cond_t *)cond)->usage_start;
266 		end_time = ((slurmdb_assoc_cond_t *)cond)->usage_end;
267 
268 		cluster_cond.cluster_list =
269 			((slurmdb_assoc_cond_t *)cond)->cluster_list;
270 	} else if ((type == CLUSTER_REPORT_UW) || (type == CLUSTER_REPORT_WU)) {
271 		start_time = ((slurmdb_wckey_cond_t *)cond)->usage_start;
272 		end_time = ((slurmdb_wckey_cond_t *)cond)->usage_end;
273 
274 		cluster_cond.cluster_list =
275 			((slurmdb_wckey_cond_t *)cond)->cluster_list;
276 	} else {
277 		error("unknown report type %d", type);
278 		return NULL;
279 	}
280 
281 	/* This needs to be done on some systems to make sure
282 	   cluster_cond isn't messed.  This has happened on some 64
283 	   bit machines and this is here to be on the safe side.
284 	*/
285 	slurmdb_report_set_start_end_time(&start_time, &end_time);
286 	cluster_cond.usage_end = end_time;
287 	cluster_cond.usage_start = start_time;
288 
289 
290 	cluster_list = acct_storage_g_get_clusters(
291 		db_conn, my_uid, &cluster_cond);
292 
293 	if (!cluster_list) {
294 		exit_code=1;
295 		fprintf(stderr, "%s: Problem with cluster query.\n",
296 			calling_name);
297 		goto end_it;
298 	}
299 
300 	if ((type == CLUSTER_REPORT_UA) || (type == CLUSTER_REPORT_AU)) {
301 		((slurmdb_assoc_cond_t *)cond)->usage_start = start_time;
302 		((slurmdb_assoc_cond_t *)cond)->usage_end = end_time;
303 		type_list = acct_storage_g_get_assocs(
304 			db_conn, my_uid, cond);
305 	} else if ((type == CLUSTER_REPORT_UW) || (type == CLUSTER_REPORT_WU)) {
306 		((slurmdb_wckey_cond_t *)cond)->usage_start = start_time;
307 		((slurmdb_wckey_cond_t *)cond)->usage_end = end_time;
308 		type_list = acct_storage_g_get_wckeys(
309 			db_conn, my_uid, cond);
310 	}
311 
312 	if (!type_list) {
313 		exit_code=1;
314 		fprintf(stderr, "%s: Problem with get query.\n", calling_name);
315 		goto end_it;
316 	}
317 
318 	if ((type == CLUSTER_REPORT_UA) || (type == CLUSTER_REPORT_AU)) {
319 		first_list = type_list;
320 		type_list = slurmdb_get_hierarchical_sorted_assoc_list(
321 			first_list, true);
322 	}
323 
324 	/* set up the structures for easy retrieval later */
325 	itr = list_iterator_create(cluster_list);
326 	type_itr = list_iterator_create(type_list);
327 	while((cluster = list_next(itr))) {
328 		/* check to see if this cluster is around during the
329 		   time we are looking at */
330 		if (!cluster->accounting_list
331 		   || !list_count(cluster->accounting_list))
332 			continue;
333 
334 		slurmdb_report_cluster = slurmdb_cluster_rec_2_report(cluster);
335 
336 		list_append(ret_list, slurmdb_report_cluster);
337 
338 		if ((type == CLUSTER_REPORT_UA) || (type == CLUSTER_REPORT_UW))
339 			slurmdb_report_cluster->user_list =
340 				list_create(slurmdb_destroy_report_user_rec);
341 		else if ((type == CLUSTER_REPORT_AU)
342 			|| (type == CLUSTER_REPORT_WU))
343 			slurmdb_report_cluster->assoc_list =
344 				list_create(slurmdb_destroy_report_assoc_rec);
345 
346 		if ((type == CLUSTER_REPORT_UA) || (type == CLUSTER_REPORT_AU))
347 			_process_assoc_type(type_itr, slurmdb_report_cluster,
348 					    cluster->name, type);
349 		else if ((type == CLUSTER_REPORT_UW)
350 			|| (type == CLUSTER_REPORT_WU))
351 			_process_wckey_type(type_itr, slurmdb_report_cluster,
352 					    cluster->name, type);
353 		list_iterator_reset(type_itr);
354 	}
355 	list_iterator_destroy(type_itr);
356 	list_iterator_destroy(itr);
357 
358 end_it:
359 	FREE_NULL_LIST(type_list);
360 	FREE_NULL_LIST(first_list);
361 	FREE_NULL_LIST(cluster_list);
362 	if (exit_code)
363 		FREE_NULL_LIST(ret_list);
364 	return ret_list;
365 }
366 
367 
slurmdb_report_cluster_account_by_user(void * db_conn,slurmdb_assoc_cond_t * assoc_cond)368 extern List slurmdb_report_cluster_account_by_user(void *db_conn,
369 	slurmdb_assoc_cond_t *assoc_cond)
370 {
371 	return _process_util_by_report(db_conn,
372 				       "slurmdb_report_cluster_account_by_user",
373 				       assoc_cond, CLUSTER_REPORT_AU);
374 }
375 
slurmdb_report_cluster_user_by_account(void * db_conn,slurmdb_assoc_cond_t * assoc_cond)376 extern List slurmdb_report_cluster_user_by_account(void *db_conn,
377 	slurmdb_assoc_cond_t *assoc_cond)
378 {
379 	return _process_util_by_report(db_conn,
380 				       "slurmdb_report_cluster_user_by_account",
381 				       assoc_cond, CLUSTER_REPORT_UA);
382 }
383 
slurmdb_report_cluster_wckey_by_user(void * db_conn,slurmdb_wckey_cond_t * wckey_cond)384 extern List slurmdb_report_cluster_wckey_by_user(void *db_conn,
385 	slurmdb_wckey_cond_t *wckey_cond)
386 {
387 	return _process_util_by_report(db_conn,
388 				       "slurmdb_report_cluster_wckey_by_user",
389 				       wckey_cond, CLUSTER_REPORT_WU);
390 }
391 
slurmdb_report_cluster_user_by_wckey(void * db_conn,slurmdb_wckey_cond_t * wckey_cond)392 extern List slurmdb_report_cluster_user_by_wckey(void *db_conn,
393 	slurmdb_wckey_cond_t *wckey_cond)
394 {
395 	return _process_util_by_report(db_conn,
396 				       "slurmdb_report_cluster_user_by_wckey",
397 				       wckey_cond, CLUSTER_REPORT_UW);
398 }
399