1 /*
2 * Copyright © 2016-2017 Inria. All rights reserved.
3 *
4 * $COPYRIGHT$
5 *
6 * Additional copyrights may follow
7 * See COPYING in top-level directory.
8 *
9 * $HEADER$
10 */
11
12 #define _GNU_SOURCE /* See feature_test_macros(7) */
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <assert.h>
17 #include <dirent.h>
18 #include <libgen.h>
19
20 #include "private/netloc.h"
21 #include "netloc.h"
22
23 #define JSON_DRAW_FILE_LINK_ID "id"
24 #define JSON_DRAW_FILE_LINK_SRC "from"
25 #define JSON_DRAW_FILE_LINK_SRC_PORT "src_port"
26 #define JSON_DRAW_FILE_LINK_DST "to"
27 #define JSON_DRAW_FILE_LINK_DST_PORT "dst_port"
28 #define JSON_DRAW_FILE_LINK_WIDTH "width"
29 #define JSON_DRAW_FILE_LINK_SPEED "speed"
30 #define JSON_DRAW_FILE_LINK_GBITS "gbits"
31 #define JSON_DRAW_FILE_LINK_OTHER_WAY "reverse"
32 #define JSON_DRAW_FILE_LINK_PARTITIONS "part"
33 #define JSON_DRAW_FILE_EDGE_ID "id"
34 #define JSON_DRAW_FILE_EDGE_SRC "from"
35 #define JSON_DRAW_FILE_EDGE_DST "to"
36 #define JSON_DRAW_FILE_EDGE_GBITS "gbits"
37 #define JSON_DRAW_FILE_EDGE_LINKS "links"
38 #define JSON_DRAW_FILE_EDGE_PARTITIONS "part"
39 #define JSON_DRAW_FILE_EDGE_SUBEDGES "subedges"
40 #define JSON_DRAW_FILE_EDGE_OTHER_WAY "reverse"
41 #define JSON_DRAW_FILE_NODE_ID "id"
42 #define JSON_DRAW_FILE_NODE_EDGES "edges"
43 #define JSON_DRAW_FILE_NODE_MERGED "merged"
44 #define JSON_DRAW_FILE_NODE_SUBNODES "sub"
45 #define JSON_DRAW_FILE_NODE_PARTITIONS "part"
46 #define JSON_DRAW_FILE_NODE_DESC "desc"
47 #define JSON_DRAW_FILE_NODE_HOSTNAME "hostname"
48 #define JSON_DRAW_FILE_NODE_HWLOCTOPO "topo"
49 #define JSON_DRAW_FILE_NODE_EDGES "edges"
50 #define JSON_DRAW_FILE_NODE_TYPE "type"
51 #define JSON_DRAW_FILE_EDGES_LIST "list"
52 #define JSON_DRAW_FILE_PATH_ID "id"
53 #define JSON_DRAW_FILE_PATH_LINKS "links"
54 #define JSON_DRAW_FILE_PATHS "paths"
55
56 #define JSON_DRAW_FILE_GRAPH_TYPE "type"
57 #define JSON_DRAW_FILE_NODES "nodes"
58 #define JSON_DRAW_FILE_EDGES "edges"
59 #define JSON_DRAW_FILE_LINKS "links"
60 #define JSON_DRAW_FILE_PARTITIONS "partitions"
61 #define JSON_DRAW_FILE_HWLOCTOPOS "hwloctopos"
62
63 /******************************************************************************/
64 /* Functions to handle JSON */
65 /******************************************************************************/
66 typedef enum {
67 JSON_STRING,
68 JSON_INT,
69 JSON_FLOAT,
70 JSON_ARRAY,
71 JSON_DICT
72 } json_type;
73
74 typedef struct {
75 int num;
76 int allocated;
77 char **strings;
78 } contents_t;
79
80 typedef struct {
81 json_type type;
82 contents_t *contents;
83 } json_t;
84
contents_new(int allocated)85 static contents_t *contents_new(int allocated)
86 {
87 contents_t *contents = (contents_t *)malloc(sizeof(contents_t));
88 contents->strings = (char **)malloc(sizeof(char *[allocated]));
89 contents->allocated = allocated;
90 contents->num = 0;
91 return contents;
92 }
93
contents_add(contents_t * contents,char * string)94 static void contents_add(contents_t *contents, char *string)
95 {
96 if (contents->num == contents->allocated) {
97 if (contents->allocated)
98 {
99 char **new_strings = (char **)
100 realloc(contents->strings, sizeof(char *[2*contents->allocated]));
101 if (!new_strings)
102 return;
103 contents->strings = new_strings;
104 contents->allocated *= 2;
105 } else {
106 contents->strings = (char **) malloc(sizeof(char *[1]));
107 if (!contents->strings)
108 return;
109 contents->allocated = 1;
110 }
111 }
112 contents->strings[contents->num] = string;
113 contents->num++;
114 }
115
contents_destruct(contents_t * contents)116 static void contents_destruct(contents_t *contents)
117 {
118 free(contents->strings);
119 free(contents);
120 }
121
contents_cat(contents_t * dest,contents_t * src)122 static void contents_cat(contents_t *dest, contents_t *src)
123 {
124 int size = src->num;
125 if (dest->num+size > dest->allocated) {
126 if (dest->allocated)
127 {
128 dest->strings = (char **)
129 realloc(dest->strings, sizeof(char *[dest->allocated+size]));
130 dest->allocated += size;
131 } else {
132 dest->strings = (char **)
133 realloc(dest->strings, sizeof(char *[size]));
134 dest->allocated = size;
135 }
136 }
137 memcpy(&dest->strings[dest->num], src->strings, sizeof(char *[src->num]));
138 dest->num += src->num;
139 }
140
json_close_object(json_t * object)141 static void json_close_object(json_t *object)
142 {
143 switch (object->type) {
144 case JSON_ARRAY:
145 contents_add(object->contents, strdup("]"));
146 break;
147 case JSON_DICT:
148 contents_add(object->contents, strdup("}"));
149 break;
150 default:
151 ;
152 }
153 }
get_content_and_destroy(json_t * object)154 static contents_t *get_content_and_destroy(json_t *object)
155 {
156 contents_t *contents = object->contents;
157 json_close_object(object);
158 free(object);
159 return contents;
160 }
161
json_dict_new()162 json_t *json_dict_new()
163 {
164 json_t *dict = (json_t *)malloc(sizeof(json_t));
165 dict->type = JSON_DICT;
166 dict->contents = contents_new(3);
167 contents_add(dict->contents, strdup("{"));
168 return dict;
169 }
170
json_dict_add(json_t * dict,char * field,json_t * child)171 void json_dict_add(json_t *dict, char *field, json_t *child)
172 {
173 assert(dict->type == JSON_DICT);
174
175 if (dict->contents->num > 1) {
176 contents_add(dict->contents, strdup(","));
177 }
178 char *field_string;
179 asprintf(&field_string, "\"%s\":", field);
180 contents_add(dict->contents, field_string);
181 contents_t *child_contents = get_content_and_destroy(child);
182 contents_cat(dict->contents, child_contents);
183 contents_destruct(child_contents);
184 }
185
json_array_new()186 json_t *json_array_new()
187 {
188 json_t *array = (json_t *)malloc(sizeof(json_t));
189 array->type = JSON_ARRAY;
190 array->contents = contents_new(3);
191 contents_add(array->contents, strdup("["));
192 return array;
193 }
194
json_array_add(json_t * array,json_t * child)195 void json_array_add(json_t *array, json_t *child)
196 {
197 assert(array->type == JSON_ARRAY);
198
199 if (array->contents->num > 1) {
200 contents_add(array->contents, strdup(","));
201 }
202 contents_t *child_contents = get_content_and_destroy(child);
203 contents_cat(array->contents, child_contents);
204 contents_destruct(child_contents);
205 }
206
json_string_new(char * value)207 json_t *json_string_new(char *value)
208 {
209 json_t *string = (json_t *)malloc(sizeof(json_t));
210 string->type = JSON_STRING;
211 string->contents = contents_new(1);
212
213 char *new_value;
214 asprintf(&new_value, "\"%s\"", value);
215 contents_add(string->contents, new_value);
216 return string;
217 }
218
json_int_new(int value)219 json_t *json_int_new(int value)
220 {
221 json_t *integer = (json_t *)malloc(sizeof(json_t));
222 integer->type = JSON_INT;
223 integer->contents = contents_new(1);
224
225 char *new_value;
226 asprintf(&new_value, "%d", value);
227 contents_add(integer->contents, new_value);
228 return integer;
229 }
230
json_float_new(float value)231 json_t *json_float_new(float value)
232 {
233 json_t *real = (json_t *)malloc(sizeof(json_t));
234 real->type = JSON_FLOAT;
235 real->contents = contents_new(1);
236
237 char *new_value;
238 asprintf(&new_value, "%f", value);
239 contents_add(real->contents, new_value);
240 return real;
241 }
242
json_write(FILE * file,json_t * object)243 void json_write(FILE *file, json_t *object)
244 {
245 json_close_object(object);;
246 for (int i = 0; i < object->contents->num; i++) {
247 fprintf(file, "%s", object->contents->strings[i]);
248 }
249 }
250
json_free(json_t * object)251 void json_free(json_t *object)
252 {
253 for (int i = 0; i < object->contents->num; i++) {
254 free(object->contents->strings[i]);
255 }
256 free(object->contents->strings);
257 free(object->contents);
258 free(object);
259 }
260
261 /* End of JSON functions */
262 /******************************************************************************/
263
remove_quote(char * string)264 static char *remove_quote(char *string)
265 {
266 if (string[0] == '\'')
267 return strndup(string+1, strlen(string)-2);
268 else
269 return strndup(string, strlen(string));
270 }
271
handle_link(netloc_physical_link_t * link,json_t * json_links)272 static int handle_link(netloc_physical_link_t *link, json_t *json_links)
273 {
274 //netloc_node_t *src, *dest;
275 char *src = link->src->physical_id;
276 char *dest = link->dest->physical_id;
277
278 json_t *json_link = json_dict_new();
279 json_dict_add(json_link,
280 JSON_DRAW_FILE_LINK_ID, json_int_new(link->id));
281 json_dict_add(json_link,
282 JSON_DRAW_FILE_LINK_SRC, json_string_new(src));
283 json_dict_add(json_link,
284 JSON_DRAW_FILE_LINK_SRC_PORT, json_int_new(link->ports[0]));
285 json_dict_add(json_link,
286 JSON_DRAW_FILE_LINK_DST, json_string_new(dest));
287 json_dict_add(json_link,
288 JSON_DRAW_FILE_LINK_DST_PORT, json_int_new(link->ports[1]));
289 json_dict_add(json_link,
290 JSON_DRAW_FILE_LINK_WIDTH, json_string_new(link->width));
291 json_dict_add(json_link,
292 JSON_DRAW_FILE_LINK_SPEED, json_string_new(link->speed));
293 json_dict_add(json_link,
294 JSON_DRAW_FILE_LINK_GBITS, json_float_new(link->gbits));
295 json_dict_add(json_link,
296 JSON_DRAW_FILE_LINK_OTHER_WAY, json_int_new(link->other_way_id));
297
298 json_t *json_partitions = json_array_new();
299
300 for (unsigned int p = 0; p < netloc_get_num_partitions(link); p++)
301 {
302 int partition = netloc_get_partition(link, p);
303 json_array_add(json_partitions, json_int_new(partition));
304 }
305 json_dict_add(json_link, JSON_DRAW_FILE_LINK_PARTITIONS, json_partitions);
306
307 json_array_add(json_links, json_link);
308
309 return 0;
310 }
311
handle_edge(netloc_edge_t * edge,json_t * json_edges)312 static int handle_edge(netloc_edge_t *edge, json_t *json_edges)
313 {
314 //netloc_node_t *src, *dest;
315 char *src = edge->node->physical_id;
316 char *dest = edge->dest->physical_id;
317
318 json_t *json_edge = json_dict_new();
319
320 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_ID, json_int_new(edge->id));
321 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_SRC, json_string_new(src));
322 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_DST, json_string_new(dest));
323 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_GBITS, json_float_new(edge->total_gbits));
324 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_OTHER_WAY, json_int_new(edge->other_way->id));
325
326 /* Links */
327 json_t *json_links = json_array_new();
328 for (unsigned int l = 0; l < netloc_edge_get_num_links(edge); l++)
329 {
330 netloc_physical_link_t *link = netloc_edge_get_link(edge, l);
331 json_array_add(json_links, json_int_new(link->id));
332 }
333 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_LINKS, json_links);
334
335 /* Partition list */
336 json_t *json_partitions = json_array_new();
337 for (unsigned int p = 0; p < netloc_get_num_partitions(edge); p++)
338 {
339 int partition = netloc_get_partition(edge, p);
340 json_array_add(json_partitions, json_int_new(partition));
341 }
342 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_PARTITIONS, json_partitions);
343
344 /* Subnode edges */
345 json_t *json_subedges = json_array_new();
346 for (unsigned int s = 0; s < netloc_edge_get_num_subedges(edge); s++)
347 {
348 netloc_edge_t *subedge = netloc_edge_get_subedge(edge, s);
349 json_array_add(json_subedges, json_int_new(subedge->id));
350 handle_edge(subedge, json_edges);
351 }
352 json_dict_add(json_edge, JSON_DRAW_FILE_EDGE_SUBEDGES, json_subedges);
353
354 json_array_add(json_edges, json_edge);
355
356 return 0;
357 }
358
handle_node(netloc_node_t * node,json_t * json_nodes,json_t * json_edges,int merged)359 static int handle_node(netloc_node_t *node, json_t *json_nodes,
360 json_t *json_edges, int merged)
361 {
362 char *id = node->physical_id;
363 char *desc = remove_quote(node->description);
364 char *hostname = node->hostname;
365 int topoIdx = node->hwlocTopoIdx;
366
367 json_t *json_node = json_dict_new();
368 json_dict_add(json_node, JSON_DRAW_FILE_NODE_ID, json_string_new(id));
369 json_dict_add(json_node, JSON_DRAW_FILE_NODE_DESC, json_string_new(desc));
370 json_dict_add(json_node, JSON_DRAW_FILE_NODE_HOSTNAME, json_string_new(hostname));
371 json_dict_add(json_node, JSON_DRAW_FILE_NODE_HWLOCTOPO, json_int_new(topoIdx));
372 json_dict_add(json_node, JSON_DRAW_FILE_NODE_MERGED, json_int_new(merged));
373
374 /* Subnodes */
375 json_t *json_subnodes = json_array_new();
376 for (unsigned int s = 0; s < netloc_node_get_num_subnodes(node); s++)
377 {
378 netloc_node_t *subnode = netloc_node_get_subnode(node, s);
379 handle_node(subnode, json_nodes, json_edges, 1);
380 json_array_add(json_subnodes, json_string_new(subnode->physical_id));
381 }
382 json_dict_add(json_node, JSON_DRAW_FILE_NODE_SUBNODES, json_subnodes);
383
384 /* Edges */
385 json_t *json_edge_ids = json_array_new();
386 netloc_edge_t *edge, *edge_tmp;
387 netloc_node_iter_edges(node, edge, edge_tmp) {
388 json_array_add(json_edge_ids, json_int_new(edge->id));
389 handle_edge(edge, json_edges);
390 }
391 json_dict_add(json_node, JSON_DRAW_FILE_NODE_EDGES, json_edge_ids);
392
393 /* Partitions */
394 json_t *json_partitions = json_array_new();
395 for (unsigned int p = 0; p < netloc_get_num_partitions(node); p++)
396 {
397 int partition = netloc_get_partition(node, p);
398 json_array_add(json_partitions, json_int_new(partition));
399 }
400 json_dict_add(json_node, JSON_DRAW_FILE_NODE_PARTITIONS, json_partitions);
401
402 if (netloc_node_is_host(node)) {
403 json_dict_add(json_node, JSON_DRAW_FILE_NODE_TYPE, json_string_new("host"));
404 }
405 else if (netloc_node_is_switch(node)) {
406 json_dict_add(json_node, JSON_DRAW_FILE_NODE_TYPE, json_string_new("switch"));
407 }
408 else {
409 json_dict_add(json_node, JSON_DRAW_FILE_NODE_TYPE, json_string_new("unknown"));
410 }
411
412 json_array_add(json_nodes, json_node);
413
414 free(desc);
415 return 0;
416 }
417
handle_path(netloc_node_t * node,json_t * json_paths)418 static int handle_path(netloc_node_t *node, json_t *json_paths)
419 {
420 char *id = node->physical_id;
421
422 json_t *json_node_paths = json_dict_new();
423 json_dict_add(json_node_paths, JSON_DRAW_FILE_PATH_ID, json_string_new(id));
424
425 /* Paths */
426 json_t *json_path_list = json_array_new();
427 netloc_path_t *path, *path_tmp;
428 netloc_node_iter_paths(node, path, path_tmp) {
429 json_t *json_node_path = json_dict_new();
430 json_dict_add(json_node_path, JSON_DRAW_FILE_PATH_ID,
431 json_string_new(path->dest_id));
432
433 json_t *json_links = json_array_new();
434 netloc_physical_link_t **plink;
435 netloc_path_iter_links(path,plink) {
436 json_array_add(json_links, json_int_new((*plink)->id));
437 }
438 json_dict_add(json_node_path, JSON_DRAW_FILE_PATH_LINKS,
439 json_links);
440 json_array_add(json_path_list, json_node_path);
441 }
442 json_dict_add(json_node_paths, JSON_DRAW_FILE_PATHS, json_path_list);
443
444 json_array_add(json_paths, json_node_paths);
445
446 return 0;
447 }
448
handle_partitions(netloc_topology_t * topology,json_t * json_partitions)449 static int handle_partitions(netloc_topology_t *topology, json_t *json_partitions)
450 {
451 char **ppartition;
452 netloc_topology_iter_partitions(topology, ppartition) {
453 json_array_add(json_partitions, json_string_new(*ppartition));
454 }
455 return 0;
456 }
457
handle_topos(netloc_topology_t * topology,json_t * json_topos)458 static int handle_topos(netloc_topology_t *topology, json_t *json_topos)
459 {
460 char **ptopo;
461 netloc_topology_iter_hwloctopos(topology, ptopo) {
462 json_array_add(json_topos, json_string_new(*ptopo));
463 }
464 return 0;
465 }
466
write_json(netloc_topology_t * topology,FILE * output)467 static int write_json(netloc_topology_t *topology, FILE *output)
468 {
469 json_t *json_root = json_dict_new();
470
471 /* Graph type */
472 json_dict_add(json_root, JSON_DRAW_FILE_GRAPH_TYPE, json_string_new("tree"));
473
474 /* Nodes */
475 json_t *json_nodes = json_array_new();
476 json_t *json_edges = json_array_new();
477 netloc_node_t *node, *node_tmp;
478 HASH_ITER(hh, topology->nodes, node, node_tmp) {
479 handle_node(node, json_nodes, json_edges, 0);
480 }
481 json_dict_add(json_root, JSON_DRAW_FILE_NODES, json_nodes);
482 json_dict_add(json_root, JSON_DRAW_FILE_EDGES, json_edges);
483
484 /* Physical links */
485 json_t *json_links = json_array_new();
486 netloc_physical_link_t *link, *link_tmp;
487 HASH_ITER(hh, topology->physical_links, link, link_tmp) {
488 handle_link(link, json_links);
489 }
490 json_dict_add(json_root, JSON_DRAW_FILE_LINKS, json_links);
491
492 /* Paths */
493 json_t *json_paths = json_array_new();
494 HASH_ITER(hh, topology->nodes, node, node_tmp) {
495 handle_path(node, json_paths);
496 }
497 json_dict_add(json_root, JSON_DRAW_FILE_PATHS, json_paths);
498
499 /* Partitions */
500 json_t *json_partitions = json_array_new();
501 handle_partitions(topology, json_partitions);
502 json_dict_add(json_root, JSON_DRAW_FILE_PARTITIONS, json_partitions);
503
504 /* Hwloc topologies */
505 json_t *json_topos = json_array_new();
506 handle_topos(topology, json_topos);
507 json_dict_add(json_root, JSON_DRAW_FILE_HWLOCTOPOS, json_topos);
508
509 json_write(output, json_root);
510 json_free(json_root);
511
512 return 0;
513 }
514
netloc_to_json_draw(netloc_topology_t * topology)515 static int netloc_to_json_draw(netloc_topology_t *topology)
516 {
517 int ret;
518 static FILE *output;
519 char *node_uri = topology->topopath;
520 int basename_len = strlen(node_uri)-10;
521 char *basename = (char *)malloc((basename_len+1)*sizeof(char));
522 char *draw;
523
524 netloc_topology_read_hwloc(topology, 0, NULL);
525
526 strncpy(basename, node_uri, basename_len);
527 basename[basename_len] = '\0';
528
529 asprintf(&draw, "%s-%s.json", basename, "draw");
530 output = fopen(draw, "w");
531 free(draw);
532 if (output == NULL) {
533 perror("fopen: ");
534 ret = NETLOC_ERROR;
535 goto ERROR;
536 }
537
538 write_json(topology, output);
539
540 ret = NETLOC_SUCCESS;
541 fclose(output);
542 ERROR:
543 free(basename);
544
545 return ret;
546 }
547
read_param(int * argc,char *** argv)548 static char *read_param(int *argc, char ***argv)
549 {
550 if (!*argc)
551 return NULL;
552
553 char *ret = **argv;
554 (*argv)++;
555 (*argc)--;
556
557 return ret;
558 }
559
help(char * name,FILE * f)560 void help(char *name, FILE *f)
561 {
562 fprintf(f, "Usage: %s <path to topology directory>\n", name);
563 }
564
main(int argc,char ** argv)565 int main(int argc, char **argv)
566 {
567 char *prog_name = basename(argv[0]);
568
569 if (argc != 2) {
570 help(prog_name, stderr);
571 return -1;
572 }
573 read_param(&argc, &argv);
574
575 char *param;
576 param = read_param(&argc, &argv);
577
578 char *netlocpath;
579 if (!strcmp(param, "--help")) {
580 help(prog_name, stdout);
581 return 0;
582 } else {
583 netlocpath = param;
584 }
585
586 DIR *netlocdir = opendir(netlocpath);
587 if (!netlocdir) {
588 fprintf(stderr, "Error: Cannot open the directory <%s>.\n", netlocpath);
589 return NETLOC_ERROR;
590 }
591
592 struct dirent *dir_entry = NULL;
593 while ((dir_entry = readdir(netlocdir)) != NULL) {
594 char *topopath;
595 #ifdef _DIRENT_HAVE_D_TYPE
596 /* Skip directories if the filesystem returns a useful d_type.
597 * Otherwise, continue and let the actual opening will fail later.
598 */
599 if( DT_DIR == dir_entry->d_type ) {
600 continue;
601 }
602 #endif
603
604 /* Skip if does not end in .txt extension */
605 if( NULL == strstr(dir_entry->d_name, "-nodes.txt") ) {
606 continue;
607 }
608
609 asprintf(&topopath, "%s/%s", netlocpath, dir_entry->d_name);
610
611 netloc_topology_t *topology;
612 topology = netloc_topology_construct(topopath);
613 if (topology == NULL) {
614 fprintf(stderr, "Error: netloc_topology_construct failed\n");
615 return NETLOC_ERROR;
616 }
617
618 netloc_edge_reset_uid();
619
620 netloc_to_json_draw(topology);
621
622 netloc_topology_destruct(topology);
623 }
624 closedir(netlocdir);
625
626 return 0;
627 }
628
629