make struct list_item more generic
[actl.git] / actl.c
blobfec76e693ca76081fbb6580e70e0501d3a296c75
1 /*
2 * Copyright (c) 2016 Mohamed Aslan <maslan@sce.carleton.ca>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <pthread.h>
21 #include <err.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <sys/wait.h>
25 #include <dlfcn.h>
27 #include <hashtab.h>
28 #include <dht.h>
29 #include <libof.h>
30 #include <of10.h>
32 #include "actl.h"
33 #include "conf.h"
34 #include "utils.h"
35 #include "model.h"
36 #include "clustering.h"
39 static struct actl_ctx ctx;
42 static void
43 adapt(struct actl_ctx *c, double kpi)
45 c->target_kpi = kpi;
46 c->adaptive = 1;
49 static int
50 put(struct actl_ctx *c, const char *key, const char *val)
52 int r, w;
53 struct timespec ts;
55 phi2rw(c->target_phi, c->conf->c_nreplicas, &r, &w);
56 (void)clock_gettime(CLOCK_REALTIME, &ts);
57 return dht_put_tunable(c->node, key, val, &ts, w);
60 static int
61 get(struct actl_ctx *c, const char *key, char **val)
63 int r, w;
65 phi2rw(c->target_phi, c->conf->c_nreplicas, &r, &w);
66 return dht_get_tunable(c->node, key, val, r);
69 static void
70 init_context(struct actl_ctx *c)
72 if (!c)
73 return;
74 c->cntl = (struct of_controller *)xmalloc(sizeof(struct of_controller));
75 c->node = (struct dht_node *)xmalloc(sizeof(struct dht_node));
76 c->conf = (struct config *)xmalloc(sizeof(struct config));
77 c->adaptive = 0;
78 c->learning = 1;
79 c->adapt = adapt;
80 c->target_phi = 0;
81 c->put = put;
82 c->get = get;
83 kmeans_seq_init(&c->kseq, 10);
86 static void
87 load_app(struct actl_ctx *c)
89 if (!c)
90 return;
91 /* load application */
92 c->conf->c_app_so = dlopen(c->conf->c_app_name, RTLD_NOW);
93 if (!c->conf->c_app_so)
94 errx(1, "error loading application %s: %s", c->conf->c_app_name, dlerror());
95 /* init */
96 c->conf->c_app.a_init = dlsym(c->conf->c_app_so, "init");
97 if (!(c->conf->c_app.a_init))
98 errx(1, "error loading symbol from %s: %s", c->conf->c_app_name, dlerror());
99 /* main */
100 c->conf->c_app.a_main = dlsym(c->conf->c_app_so, "main");
101 if (!c->conf->c_app.a_main)
102 errx(1, "error loading symbol from %s: %s", c->conf->c_app_name, dlerror());
103 /* handler */
104 c->conf->c_app.a_handler = dlsym(c->conf->c_app_so, "handler");
105 if (!c->conf->c_app.a_handler)
106 errx(1, "error loading symbol from %s: %s", c->conf->c_app_name, dlerror());
107 /* handler */
108 c->conf->c_app.a_kpi = dlsym(c->conf->c_app_so, "kpi");
109 if (!c->conf->c_app.a_kpi)
110 errx(1, "error loading symbol from %s: %s", c->conf->c_app_name, dlerror());
113 static void
114 poll_kpi(struct of_controller *ctl)
116 double cur_kpi, cur_phi;
118 if (ctx.conf->c_app_so && ctx.adaptive) {
119 cur_kpi = ctx.conf->c_app.a_kpi();
120 if (ctx.learning) {
121 kmeans_seq_insert(&ctx.kseq, cur_kpi, ctx.target_phi);
122 printf("[INFO] learn KPI = %lf -> Phi = %lf.\n", cur_kpi, ctx.target_phi);
123 } else {
124 cur_phi = kmeans_seq_find(&ctx.kseq, cur_kpi);
125 printf("[INFO] KPI = (target %lf, current %lf), Phi = %lf.\n", ctx.target_kpi, cur_kpi, cur_phi);
126 f_log(ctx.conf->c_logfp, "%lf, %lf, %lf, %lf\n", ctx.target_kpi, ctx.target_phi, cur_kpi, cur_phi);
131 static void
132 learn_timer(struct of_controller *ctl)
134 if (ctx.conf->c_app_so && ctx.adaptive && ctx.learning) {
135 ctx.conf->c_ltime -= ctx.learn_perval;
136 if (ctx.conf->c_ltime <= 0) {
137 ctx.learning = 0;
138 printf("[INFO] learning finished.\n");
139 kmeans_seq_print(&ctx.kseq, "LEARN");
141 ctx.target_phi += 0.1;
145 static void *
146 dht_thread(void *arg)
148 dht_event_loop(ctx.node);
149 return NULL;
152 static void *
153 controller_thread(void *arg)
155 /* init default app */
156 if (ctx.conf->c_app_so) {
157 optind = 0; /* make sure apps can use getopt(3) or getopt_long(3) */
158 ctx.conf->c_app.a_init(&ctx);
159 printf("[INFO] adaptation period is %d sec.\n", ctx.conf->c_atime);
160 ctx.conf->c_app.a_main(ctx.conf->c_app_argc, ctx.conf->c_app_argv);
162 ctx.learn_perval = ctx.conf->c_ltime / 10;
163 ctx.cntl->timer(ctx.cntl, ctx.learn_perval * 1000, learn_timer);
164 /* poll application-specific KPI */
165 ctx.cntl->timer(ctx.cntl, ctx.conf->c_atime * 1000, poll_kpi);
166 ctx.cntl->loop(ctx.cntl);
167 return NULL;
170 static void
171 handler(struct of_controller *ctl, struct of_event *ev)
173 #ifdef DEBUG
174 printf("actl: handler.\n");
175 #endif
176 if (ctx.conf->c_app_so)
177 ctx.conf->c_app.a_handler(ev);
181 main(int argc, char **argv)
183 char *homedir, *conf_file;
184 int ch, flag_altconf = 0, flag_error = 0;
185 pthread_t dtid, ctid;
188 /* init global context */
189 init_context(&ctx);
191 /* parse command line options */
192 while ((ch = getopt(argc, argv, "c:")) != -1) {
193 switch (ch) {
194 case 'c':
195 flag_altconf = 1;
196 conf_file = strdup(optarg);
197 break;
198 default:
199 flag_error = 1;
200 break;
203 argc -= optind;
204 argv += optind;
206 if (flag_error) {
207 return EXIT_FAILURE; /* TODO: usage */
209 if (!flag_altconf) {
210 if ((homedir = getenv("HOME")) == NULL) {
211 /* TODO: look in current directory */
212 errx(1, "failed to read environmental variable \'$HOME\'");
214 asprintf(&conf_file, "%s/%s", homedir, CONFFILENAME); /* XXX: null */
216 /* parse config file */
217 if (parse_config(ctx.conf, conf_file))
218 errx(1, "error parsing config file.");
220 /* open log file */
221 if (ctx.conf->c_logfile) {
222 ctx.conf->c_logfp = fopen(ctx.conf->c_logfile, "w");
223 if (!ctx.conf->c_logfp)
224 errx(1, "error creating log file.");
227 /* load application */
228 load_app(&ctx);
230 #ifdef DEBUG
231 printf("config info...\n");
232 printf("\tnode id \'%s\', control port \'%d\', switch port \'%d\'.\n",
233 ctx.conf->c_nodeid, ctx.conf->c_co_port, ctx.conf->c_sw_port);
234 printf("\topenflow version \'%s\'.\n", ctx.conf->c_ofp_verstr);
235 printf("\tnumber of replicas \'%d\'.\n", ctx.conf->c_nreplicas);
236 #endif
238 /* initialize DHT */
239 if (!dht_init(ctx.node, ctx.conf->c_nodeid, ctx.conf->c_co_port, ctx.conf->c_nreplicas, 0, NULL))
240 errx(1, "dht_init");
241 else
242 printf("dht init successful.\n");
243 pthread_create(&dtid, NULL, dht_thread, NULL);
245 /* initialize OF controller */
246 if (of_controller_init(ctx.cntl, ctx.conf->c_sw_port, ctx.conf->c_ofp()))
247 errx(1, "init failed.");
248 else
249 printf("controller init successful.\n");
250 ctx.cntl->handler(ctx.cntl, handler);
251 pthread_create(&ctid, NULL, controller_thread, NULL);
253 /* wait */
254 pthread_join(dtid, NULL);
255 pthread_join(ctid, NULL);
257 fclose(ctx.conf->c_logfp);
258 return 0;