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