Whamcloud - gitweb
e174fe9eeb5a3d91f6743fb31097456eac7042d7
[fs/lustre-release.git] / lustre / tests / lutf / src / lutf.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <netinet/tcp.h>
9 #include <arpa/inet.h>
10 #include <netdb.h>
11 #include <pthread.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <strings.h>
18 #include <sys/time.h>
19 #include <sys/socket.h>
20 #include "lnetconfig/cyaml.h"
21 #include "lutf_listener.h"
22 #include "lutf_message.h"
23 #include "lutf_python.h"
24 #include "lutf.h"
25
26 #define HB_TIMEOUT      2
27
28 struct in_addr g_local_ip;
29 FILE *out;
30 char *outlog;
31
32 /*externs needed by getopt lib*/
33 extern char *optarg;
34 extern int optind;
35
36 static void
37 lutf_help_usage(const struct option *long_options, const char *description[])
38 {
39         int i = 0;
40
41         fprintf(stderr, BOLDCYAN "LUTF Runs in two modes: "
42                 RESET BOLDMAGENTA "Master" RESET BOLDCYAN " or " BOLDRED "Agent\n\n"
43                 BOLDMAGENTA
44                 "Master Mode\n"
45                 "    . Runs on the Test Master node and controls all agents\n"
46                 BOLDRED
47                 "Agent Mode:\n"
48                 "    . Runs on the Nodes Under Test\n\n"
49                 BOLDGREEN
50                 "Look at lutf/python/config/lutf_cfg_sample.yaml for a sample "
51                 "LUTF configuration\n\n"
52                 RESET
53                 "Options:\n");
54
55         while ((long_options[i].name != NULL) && (description[i] != NULL)) {
56                 fprintf(stderr, "\t-%c or --%s %s\n",
57                         (char) long_options[i].val,
58                         long_options[i].name,
59                         description[i]);
60                 i++;
61         }
62
63         fprintf(stderr, "\n");
64 }
65
66 static struct cYAML *get_value(struct cYAML *head, char *key)
67 {
68         struct cYAML *child = head;
69
70         while (child != NULL) {
71                 if (strcmp(child->cy_string, key) == 0)
72                         return child;
73                 child = child->cy_next;
74         }
75
76         return NULL;
77 }
78
79 static
80 lutf_rc_t hostname_to_ip(char *hostname, char *ip, int len)
81 {
82         struct addrinfo hints, *servinfo, *p;
83         struct sockaddr_in *h;
84         int rv;
85
86         memset(&hints, 0, sizeof(hints));
87         hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
88         hints.ai_socktype = SOCK_STREAM;
89
90         if ((rv = getaddrinfo(hostname, "http", &hints, &servinfo)) != 0) {
91                 PERROR("getaddrinfo: %s\n", gai_strerror(rv));
92                 return EN_LUTF_RC_BAD_ADDR;
93         }
94
95         // loop through all the results and connect to the first we can
96         memset(ip, 0, len);
97         for (p = servinfo; p != NULL; p = p->ai_next) {
98                 h = (struct sockaddr_in *) p->ai_addr;
99                 strncpy(ip, inet_ntoa(h->sin_addr), len-1);
100         }
101
102         freeaddrinfo(servinfo); // all done with this structure
103         return EN_LUTF_RC_OK;
104 }
105
106 static
107 lutf_rc_t extract_config_parameters(struct cYAML *config_tree,
108                                     lutf_config_params_t *cfg,
109                                     char **elem)
110 {
111         struct in_addr addr;
112         struct cYAML *head;
113         struct cYAML *tmp;
114         char maddr[24];
115         lutf_rc_t rc;
116
117         head = config_tree->cy_child;
118
119         if (strcmp(head->cy_string, "lutf") != 0) {
120                 *elem = "lutf";
121                 return EN_LUTF_RC_BAD_PARAM;
122         }
123
124         /* go  to the list of elements we need to browse */
125         head = head->cy_child;
126
127         tmp = get_value(head, "shell");
128         if (tmp) {
129                 if (tmp->cy_type == CYAML_TYPE_STRING) {
130                         if (strcmp(tmp->cy_valuestring,
131                                    INTERACTIVE) == 0) {
132                                 cfg->shell = EN_LUTF_RUN_INTERACTIVE;
133                         } else if (strcmp(tmp->cy_valuestring,
134                                         BATCH) == 0) {
135                                 cfg->shell = EN_LUTF_RUN_BATCH;
136                         } else if (strcmp(tmp->cy_valuestring,
137                                         DAEMON) == 0) {
138                                 cfg->shell = EN_LUTF_RUN_DAEMON;
139                         } else {
140                                 *elem = "shell";
141                                 return EN_LUTF_RC_BAD_PARAM;
142                         }
143                 } else {
144                         *elem = "shell";
145                         return EN_LUTF_RC_BAD_PARAM;
146                 }
147         } else {
148                 *elem = "shell";
149                 return EN_LUTF_RC_MISSING_PARAM;
150         }
151
152         tmp = get_value(head, "agent");
153         if (tmp) {
154                 if (tmp->cy_type == CYAML_TYPE_FALSE)
155                         cfg->l_info.type = EN_LUTF_MASTER;
156                 else if (tmp->cy_type == CYAML_TYPE_TRUE)
157                         cfg->l_info.type = EN_LUTF_AGENT;
158                 else {
159                         *elem = "agent";
160                         return EN_LUTF_RC_BAD_PARAM;
161                 }
162         } else {
163                 cfg->l_info.type = EN_LUTF_MASTER;
164         }
165
166         tmp = get_value(head, "telnet-port");
167         if (tmp) {
168                 if (tmp->cy_type == CYAML_TYPE_NUMBER)
169                         cfg->l_info.hb_info.agent_telnet_port = tmp->cy_valueint;
170                 else {
171                         *elem = "telnet-port";
172                         return EN_LUTF_RC_BAD_PARAM;
173                 }
174         } else {
175                 cfg->l_info.hb_info.agent_telnet_port = -1;
176         }
177
178         tmp = get_value(head, "master-address");
179         if (tmp) {
180                 if (tmp->cy_type == CYAML_TYPE_STRING) {
181                         if (!inet_aton(tmp->cy_valuestring, &addr)) {
182                                 /* maybe it's a host name so let's try
183                                  * that out
184                                  */
185                                 rc = EN_LUTF_RC_BAD_ADDR;
186                                 if ((rc = hostname_to_ip(tmp->cy_valuestring, maddr,
187                                                          sizeof(maddr)))
188                                     != EN_LUTF_RC_OK) {
189                                         *elem = "master-address";
190                                         return rc;
191                                 } else if (!inet_aton(maddr, &addr)) {
192                                         *elem = "master-address";
193                                         return rc;
194                                 }
195                         }
196                         cfg->l_info.hb_info.master_address.sin_addr = addr;
197                 } else {
198                         *elem = "master-address";
199                         return EN_LUTF_RC_BAD_PARAM;
200                 }
201         } else if (cfg->l_info.type == EN_LUTF_AGENT) {
202                 *elem = "master-address";
203                 return EN_LUTF_RC_MISSING_PARAM;
204         }
205
206         tmp = get_value(head, "master-port");
207         if (tmp) {
208                 if (tmp->cy_type == CYAML_TYPE_NUMBER) {
209                         cfg->l_info.hb_info.master_address.sin_port = tmp->cy_valueint;
210                         cfg->l_info.listen_port = tmp->cy_valueint;
211                 } else {
212                         *elem = "master-port";
213                         return EN_LUTF_RC_BAD_PARAM;
214                 }
215         } else {
216                 cfg->l_info.hb_info.master_address.sin_port = DEFAULT_MASTER_PORT;
217                 cfg->l_info.listen_port = DEFAULT_MASTER_PORT;
218         }
219         cfg->l_info.hb_info.master_address.sin_family = AF_INET;
220
221         tmp = get_value(head, "lutf-path");
222         if (tmp) {
223                 if (tmp->cy_type == CYAML_TYPE_STRING)
224                         cfg->lutf_path = tmp->cy_valuestring;
225                 else {
226                         *elem = "lutf-path";
227                         return EN_LUTF_RC_BAD_PARAM;
228                 }
229         } else {
230                 *elem = "lutf-path";
231                 return EN_LUTF_RC_MISSING_PARAM;
232         }
233
234         tmp = get_value(head, "py-path");
235         if (tmp) {
236                 if (tmp->cy_type == CYAML_TYPE_STRING)
237                         cfg->py_path = tmp->cy_valuestring;
238                 else {
239                         *elem = "py-path";
240                         return EN_LUTF_RC_BAD_PARAM;
241                 }
242         }
243
244         tmp = get_value(head, "node-name");
245         if (tmp) {
246                 if (tmp->cy_type == CYAML_TYPE_STRING) {
247                         strncpy(cfg->l_info.hb_info.node_name, tmp->cy_valuestring,
248                                 MAX_STR_LEN);
249                         cfg->l_info.hb_info.node_name[MAX_STR_LEN - 1] = '\0';
250                 } else {
251                         *elem = "node-name";
252                         return EN_LUTF_RC_BAD_PARAM;
253                 }
254         } else {
255                 strncpy(cfg->l_info.hb_info.node_name, TEST_ROLE_GRC,
256                         MAX_STR_LEN);
257                 cfg->l_info.hb_info.node_name[MAX_STR_LEN - 1] = '\0';
258         }
259
260         tmp = get_value(head, "master-name");
261         if (tmp) {
262                 if (tmp->cy_type == CYAML_TYPE_STRING)
263                         cfg->master_name = tmp->cy_valuestring;
264                 else
265                         return EN_LUTF_RC_BAD_PARAM;
266         } else if (cfg->l_info.type == EN_LUTF_AGENT) {
267                 *elem = "master-name";
268                 return EN_LUTF_RC_MISSING_PARAM;
269         }
270
271         tmp = get_value(head, "suite");
272         if (tmp && cfg->l_info.type == EN_LUTF_MASTER) {
273                 if (tmp->cy_type == CYAML_TYPE_STRING)
274                         if (strlen(tmp->cy_valuestring) > 0)
275                                 cfg->suite = tmp->cy_valuestring;
276                         else
277                                 cfg->suite = NULL;
278                 else {
279                         *elem = "suite";
280                         return EN_LUTF_RC_BAD_PARAM;
281                 }
282         }
283
284         tmp = get_value(head, "script");
285         if (tmp && cfg->l_info.type == EN_LUTF_MASTER) {
286                 if (tmp->cy_type == CYAML_TYPE_STRING)
287                         if (strlen(tmp->cy_valuestring) > 0)
288                                 cfg->script = tmp->cy_valuestring;
289                         else
290                                 cfg->script = NULL;
291                 else {
292                         *elem = "script";
293                         return EN_LUTF_RC_BAD_PARAM;
294                 }
295         }
296
297         if (!cfg->suite && cfg->script) {
298                 *elem = "suite";
299                 return EN_LUTF_RC_BAD_PARAM;
300         }
301
302         tmp = get_value(head, "pattern");
303         if (tmp) {
304                 if (tmp->cy_type == CYAML_TYPE_STRING)
305                         if (strlen(tmp->cy_valuestring) > 0)
306                                 cfg->pattern = tmp->cy_valuestring;
307                         else
308                                 cfg->pattern = "*";
309                 else {
310                         *elem = "pattern";
311                         return EN_LUTF_RC_BAD_PARAM;
312                 }
313         } else {
314                 cfg->pattern = "*";
315         }
316
317         tmp = get_value(head, "results");
318         if (tmp) {
319                 if (tmp->cy_type == CYAML_TYPE_STRING)
320                         cfg->results_file = tmp->cy_valuestring;
321                 else {
322                         *elem = "results";
323                         return EN_LUTF_RC_BAD_PARAM;
324                 }
325         } else {
326                 cfg->results_file = "lutf_def_results";
327         }
328
329         tmp = get_value(head, "agent-list");
330         if (tmp) {
331                 if (cYAML_is_sequence(tmp))
332                         cfg->agents = tmp;
333                 else {
334                         *elem = "agent-list";
335                         return EN_LUTF_RC_BAD_PARAM;
336                 }
337         } else {
338                 cfg->agents = NULL;
339         }
340
341         tmp = get_value(head, "tmp-dir");
342         if (tmp) {
343                 if (tmp->cy_type == CYAML_TYPE_STRING)
344                         cfg->tmp_dir = tmp->cy_valuestring;
345                 else {
346                         *elem = "tmp-dir";
347                         return EN_LUTF_RC_BAD_PARAM;
348                 }
349         } else {
350                 cfg->tmp_dir = "/tmp/lutf/";
351         }
352
353         return EN_LUTF_RC_OK;
354 }
355
356 int
357 main(int argc, char *argv[])
358 {
359         int cOpt;
360         pthread_t l_thread_id;
361         lutf_rc_t rc;
362         int trc;
363         char *config_file = NULL;
364         char *elem = NULL;
365         struct cYAML *config_tree;
366         struct cYAML *err_rc = NULL;
367
368         out = stdout;
369
370         memset(&g_lutf_cfg, 0, sizeof(g_lutf_cfg));
371
372         /* If followed by a ':', the option requires an argument*/
373         const char *const short_options = "c:h";
374         const struct option long_options[] = {
375                 {.name = "config", .has_arg = required_argument, .val = 'c'},
376                 {.name = "help", .has_arg = no_argument, .val = 'h'},
377                 {NULL, 0, NULL, 0}
378         };
379
380         const char *description[] = {
381                 /*'c'*/":\n\t\tYAML config file",
382                 /*'h'*/":\n\t\tPrint this help",
383                 NULL
384         };
385
386         /* sanity check */
387         if (argc < 1) {
388                 lutf_help_usage(long_options, description);
389                 exit(LUTF_EXIT_ERR_STARTUP);
390         }
391
392         /*now process command line arguments*/
393         if (argc > 1) {
394                 while ((cOpt = getopt_long(argc, argv,
395                                            short_options,
396                                            long_options,
397                                            NULL)) != -1) {
398                         switch (cOpt) {
399                         case 'c':
400                                 config_file = optarg;
401                                 break;
402                         case 'h':
403                                 lutf_help_usage(long_options, description);
404                                 exit(LUTF_EXIT_NORMAL);
405                         default:
406                                 PERROR("Bad parameter");
407                                 exit(LUTF_EXIT_ERR_BAD_PARAM);
408                                 break;
409                         }
410                 }
411         }
412
413         if (!config_file) {
414                 lutf_help_usage(long_options, description);
415                 exit(LUTF_EXIT_ERR_BAD_PARAM);
416         }
417
418         g_lutf_cfg.cfg_path = config_file;
419
420         config_tree = cYAML_build_tree(config_file, NULL, 0, &err_rc, false);
421         if (!config_tree) {
422                 PERROR("Failed to parse config file: %s", config_file);
423                 exit(LUTF_EXIT_ERR_BAD_PARAM);
424         }
425
426         rc = extract_config_parameters(config_tree, &g_lutf_cfg, &elem);
427         if (rc != EN_LUTF_RC_OK) {
428                 PERROR("Parsing configuration failed on %s with %s",
429                        elem, lutf_rc2str(rc));
430                 exit(LUTF_EXIT_ERR_BAD_PARAM);
431         }
432
433         outlog = calloc(strlen(g_lutf_cfg.tmp_dir) + strlen(OUT_LOG_NAME) + 2, 1);
434
435         if (!outlog) {
436                 PERROR("out of memory");
437                 exit(LUTF_EXIT_ERR_STARTUP);
438         }
439
440         sprintf(outlog, "%s/%s", g_lutf_cfg.tmp_dir, OUT_LOG_NAME);
441
442         out = fopen(outlog, "w");
443
444         if (!out) {
445                 fprintf(stderr, "Failed to open log files: %s\n",
446                         outlog);
447                 exit(LUTF_EXIT_ERR_STARTUP);
448         }
449
450         if (g_lutf_cfg.shell == EN_LUTF_RUN_DAEMON) {
451                 pid_t process_id = 0;
452                 pid_t sid = 0;
453
454                 /* create the child process */
455                 process_id = fork();
456                 if (process_id < 0) {
457                         PERROR("Failed to run lutf as deamon");
458                         exit(LUTF_EXIT_ERR_DEAMEON_STARTUP);
459                 }
460
461                 if (process_id > 0) {
462                         /*
463                          * We're in the parent process so let's kill it
464                          * off
465                          */
466                         PDEBUG("Shutting down parent process");
467                         exit(LUTF_EXIT_NORMAL);
468                 }
469
470                 umask(0);
471                 sid = setsid();
472                 if (sid < 0) {
473                         PERROR("forking child failed");
474                         exit(LUTF_EXIT_ERR_DEAMEON_STARTUP);
475                 }
476
477                 rc = chdir("/");
478                 close(STDIN_FILENO);
479                 close(STDOUT_FILENO);
480                 close(STDERR_FILENO);
481                 if (rc) {
482                         PERROR("chdir failed");
483                         exit(LUTF_EXIT_ERR_DEAMEON_STARTUP);
484                 }
485         }
486
487         /*
488          * Spawn the listener thread if we are in Master Mode.
489          * The listener thread listens for Heart beats and deals
490          * with maintaining the health of the agents. If an agent
491          * dies and comes back again, then we know how to deal
492          * with it.
493          */
494         trc = pthread_create(&l_thread_id, NULL,
495                              lutf_listener_main,
496                              &g_lutf_cfg.l_info);
497         if (trc) {
498                 PERROR("Failed to start thread");
499                 exit(LUTF_EXIT_ERR_THREAD_STARTUP);
500         }
501
502         /* spawn listener thread iff running in Master mode */
503         rc = python_init();
504         if (rc) {
505                 PERROR("Failed to initialize Python Module");
506                 if (rc == EN_LUTF_RC_ERR_THREAD_STARTUP)
507                         exit(LUTF_EXIT_ERR_THREAD_STARTUP);
508                 else
509                         exit(LUTF_EXIT_ERR_STARTUP);
510         }
511
512         pthread_join(l_thread_id, NULL);
513
514         fclose(out);
515
516         cYAML_free_tree(config_tree);
517
518         return 0;
519 }