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