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