simplify logic
[oweals/gnunet.git] / src / ats / gnunet-service-ats_plugins.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011-2014 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  option) any later version.
9
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file ats/gnunet-service-ats_plugins.c
23  * @brief ats service plugin management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_plugin.h"
29 #include "gnunet-service-ats_connectivity.h"
30 #include "gnunet-service-ats_performance.h"
31 #include "gnunet-service-ats_preferences.h"
32 #include "gnunet-service-ats_plugins.h"
33 #include "gnunet-service-ats_scheduling.h"
34 #include "gnunet-service-ats_normalization.h"
35
36
37 /**
38  * Configured ATS solver
39  */
40 static int ats_mode;
41
42 /**
43  * Solver handle.
44  */
45 static struct GNUNET_ATS_SolverFunctions *sf;
46
47 /**
48  * Solver environment.
49  */
50 static struct GNUNET_ATS_PluginEnvironment env;
51
52 /**
53  * Solver plugin name as string
54  */
55 static char *plugin;
56
57
58 /**
59  * The preference changed for a peer, update solver.
60  *
61  * @param peer the peer
62  * @param kind the ATS kind
63  * @param pref_rel the new relative preference value
64  */
65 void
66 GAS_normalized_preference_changed (const struct GNUNET_PeerIdentity *peer,
67                                    enum GNUNET_ATS_PreferenceKind kind,
68                                    double pref_rel)
69 {
70   /* Tell solver about update */
71   sf->s_pref (sf->cls, peer, kind, pref_rel);
72 }
73
74
75 /**
76  * The relative value for a property changed
77  *
78  * @param address the peer
79  * @param type the ATS type
80  * @param prop_rel the new relative preference value
81  */
82 void
83 GAS_normalized_property_changed (struct ATS_Address *address,
84                                  uint32_t type,
85                                  double prop_rel)
86 {
87   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
88               "Normalized property %s for peer `%s' changed to %.3f \n",
89               GNUNET_ATS_print_property_type (type),
90               GNUNET_i2s (&address->peer),
91               prop_rel);
92   sf->s_address_update_property (sf->cls,
93                                  address,
94                                  type,
95                                  0,
96                                  prop_rel);
97 }
98
99
100 /**
101  * Solver information callback
102  *
103  * @param cls the closure
104  * @param op the operation
105  * @param status operation status
106  * @param add additional information
107  */
108 static void
109 solver_info_cb (void *cls,
110                 enum GAS_Solver_Operation op,
111                 enum GAS_Solver_Status status,
112                 enum GAS_Solver_Additional_Information add)
113 {
114   char *add_info;
115
116   switch (add) {
117     case GAS_INFO_NONE:
118       add_info = "GAS_INFO_NONE";
119       break;
120     case GAS_INFO_FULL:
121       add_info = "GAS_INFO_MLP_FULL";
122       break;
123     case GAS_INFO_UPDATED:
124       add_info = "GAS_INFO_MLP_UPDATED";
125       break;
126     case GAS_INFO_PROP_ALL:
127       add_info = "GAS_INFO_PROP_ALL";
128       break;
129     case GAS_INFO_PROP_SINGLE:
130       add_info = "GAS_INFO_PROP_SINGLE";
131       break;
132     default:
133       add_info = "INVALID";
134       break;
135   }
136   switch (op)
137   {
138     case GAS_OP_SOLVE_START:
139       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
140           "Solver notifies `%s' with result `%s' `%s'\n", "GAS_OP_SOLVE_START",
141           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL", add_info);
142       return;
143     case GAS_OP_SOLVE_STOP:
144       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
145           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_STOP",
146           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL", add_info);
147       return;
148
149     case GAS_OP_SOLVE_SETUP_START:
150       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
151           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_START",
152           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
153       return;
154
155     case GAS_OP_SOLVE_SETUP_STOP:
156       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
157           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_STOP",
158           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
159       return;
160
161     case GAS_OP_SOLVE_MLP_LP_START:
162       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
163           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_START",
164           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
165       return;
166     case GAS_OP_SOLVE_MLP_LP_STOP:
167       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
168           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_STOP",
169           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
170       return;
171
172     case GAS_OP_SOLVE_MLP_MLP_START:
173       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
174           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_START",
175           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
176       return;
177     case GAS_OP_SOLVE_MLP_MLP_STOP:
178       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
179           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_STOP",
180           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
181       return;
182     case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START:
183       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
184           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_START",
185           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
186       return;
187     case GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP:
188       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
189           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP",
190           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
191       return;
192     default:
193       break;
194     }
195 }
196
197
198 /**
199  * Callback for solver to notify about assignment changes
200  *
201  * @param cls NULL
202  * @param address the address with changes
203  */
204 static void
205 bandwidth_changed_cb (void *cls,
206                       struct ATS_Address *address)
207 {
208   uint32_t diff_out;
209   uint32_t diff_in;
210
211   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
212               "Bandwidth assignment changed for peer %s \n",
213               GNUNET_i2s (&address->peer));
214
215   /* Notify performance clients about changes to address */
216   GAS_performance_notify_all_clients (&address->peer,
217                                       address->plugin,
218                                       address->addr,
219                                       address->addr_len,
220                                       address->active,
221                                       address->atsi,
222                                       address->atsi_count,
223                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
224                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
225
226   if ( (0 == address->assigned_bw_in) &&
227        (0 == address->assigned_bw_out) )
228   {
229     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230                "Telling transport to disconnect peer `%s'\n",
231                 GNUNET_i2s (&address->peer));
232
233     /* Notify scheduling clients about suggestion */
234     GAS_scheduling_transmit_address_suggestion (&address->peer,
235                                                 address->session_id,
236                                                 GNUNET_BANDWIDTH_ZERO,
237                                                 GNUNET_BANDWIDTH_ZERO);
238     return;
239   }
240
241   /* Do bandwidth stability check */
242   diff_out = abs (address->assigned_bw_out - address->last_notified_bw_out);
243   diff_in = abs (address->assigned_bw_in - address->last_notified_bw_in);
244
245   if ( (diff_out < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) &&
246        (diff_in < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) )
247     return;
248
249   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
250       "Sending bandwidth update for peer `%s': %u %u\n",
251       GNUNET_i2s (&address->peer), address->assigned_bw_out,
252       address->assigned_bw_out);
253
254   /* *Notify scheduling clients about suggestion */
255   GAS_scheduling_transmit_address_suggestion (&address->peer,
256                                               address->session_id,
257                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
258                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
259
260   address->last_notified_bw_out = address->assigned_bw_out;
261   address->last_notified_bw_in = address->assigned_bw_in;
262 }
263
264
265 /**
266  * Load quotas for networks from configuration
267  *
268  * @param cfg configuration handle
269  * @param out_dest where to write outbound quotas
270  * @param in_dest where to write inbound quotas
271  * @param dest_length length of inbound and outbound arrays
272  * @return number of networks loaded
273  */
274 static unsigned int
275 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg,
276              unsigned long long *out_dest,
277              unsigned long long *in_dest,
278              int dest_length)
279 {
280   char *entry_in = NULL;
281   char *entry_out = NULL;
282   char *quota_out_str;
283   char *quota_in_str;
284   int c;
285   int res;
286
287   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
288   {
289     in_dest[c] = 0;
290     out_dest[c] = 0;
291     GNUNET_asprintf (&entry_out,
292                      "%s_QUOTA_OUT",
293                      GNUNET_ATS_print_network_type (c));
294     GNUNET_asprintf (&entry_in,
295                      "%s_QUOTA_IN",
296                      GNUNET_ATS_print_network_type (c));
297
298     /* quota out */
299     if (GNUNET_OK
300         == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", entry_out,
301             &quota_out_str))
302     {
303       res = GNUNET_NO;
304       if (0 == strcmp (quota_out_str, GNUNET_ATS_MaxBandwidthString))
305       {
306         out_dest[c] = GNUNET_ATS_MaxBandwidth;
307         res = GNUNET_YES;
308       }
309       if ((GNUNET_NO == res)
310           && (GNUNET_OK
311               == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
312                   &out_dest[c])))
313         res = GNUNET_YES;
314       if ((GNUNET_NO == res)
315           && (GNUNET_OK
316               == GNUNET_CONFIGURATION_get_value_number (cfg, "ats", entry_out,
317                   &out_dest[c])))
318         res = GNUNET_YES;
319
320       if (GNUNET_NO == res)
321       {
322         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
323                    _("Could not load quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
324                    GNUNET_ATS_print_network_type (c),
325                    quota_out_str,
326                    GNUNET_ATS_DefaultBandwidth);
327         out_dest[c] = GNUNET_ATS_DefaultBandwidth;
328       }
329       else
330       {
331         GNUNET_log(GNUNET_ERROR_TYPE_INFO,
332                    _("Outbound quota configure for network `%s' is %llu\n"),
333                    GNUNET_ATS_print_network_type (c),
334                    out_dest[c]);
335       }
336       GNUNET_free(quota_out_str);
337     }
338     else
339     {
340       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
341                  _("No outbound quota configured for network `%s', assigning default bandwidth %llu\n"),
342                  GNUNET_ATS_print_network_type (c),
343                  GNUNET_ATS_DefaultBandwidth);
344       out_dest[c] = GNUNET_ATS_DefaultBandwidth;
345     }
346
347     /* quota in */
348     if (GNUNET_OK
349         == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", entry_in,
350             &quota_in_str))
351     {
352       res = GNUNET_NO;
353       if (0 == strcmp (quota_in_str, GNUNET_ATS_MaxBandwidthString))
354       {
355         in_dest[c] = GNUNET_ATS_MaxBandwidth;
356         res = GNUNET_YES;
357       }
358       if ((GNUNET_NO == res)
359           && (GNUNET_OK
360               == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &in_dest[c])))
361         res = GNUNET_YES;
362       if ((GNUNET_NO == res)
363           && (GNUNET_OK
364               == GNUNET_CONFIGURATION_get_value_number (cfg, "ats", entry_in,
365                   &in_dest[c])))
366         res = GNUNET_YES;
367
368       if (GNUNET_NO == res)
369       {
370         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371                     _("Could not load quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
372                     GNUNET_ATS_print_network_type (c),
373                     quota_in_str,
374                     GNUNET_ATS_DefaultBandwidth);
375         in_dest[c] = GNUNET_ATS_DefaultBandwidth;
376       }
377       else
378       {
379         GNUNET_log(GNUNET_ERROR_TYPE_INFO,
380                    _("Inbound quota configured for network `%s' is %llu\n"),
381                    GNUNET_ATS_print_network_type (c),
382                    in_dest[c]);
383       }
384       GNUNET_free(quota_in_str);
385     }
386     else
387     {
388       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
389                   _("No outbound quota configure for network `%s', assigning default bandwidth %llu\n"),
390                   GNUNET_ATS_print_network_type (c),
391                   GNUNET_ATS_DefaultBandwidth);
392       in_dest[c] = GNUNET_ATS_DefaultBandwidth;
393     }
394     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395                 "Loaded quota for network `%s' (in/out): %llu %llu\n",
396                 GNUNET_ATS_print_network_type (c),
397                 in_dest[c],
398                 out_dest[c]);
399     GNUNET_free(entry_out);
400     GNUNET_free(entry_in);
401   }
402   return GNUNET_ATS_NetworkTypeCount;
403 }
404
405
406 /**
407  * Initialize plugins subsystem.
408  *
409  * @param cfg configuration to use
410  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (failed to load
411  *         solver plugin)
412  */
413 int
414 GAS_plugins_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
415 {
416   unsigned long long quotas_in[GNUNET_ATS_NetworkTypeCount];
417   unsigned long long quotas_out[GNUNET_ATS_NetworkTypeCount];
418   char *mode_str;
419   char *plugin_short;
420   int c;
421
422   /* Figure out configured solution method */
423   if (GNUNET_SYSERR ==
424       GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
425   {
426     GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
427                "No resource assignment method configured, using proportional approach\n");
428     ats_mode = MODE_PROPORTIONAL;
429   }
430   else
431   {
432     for (c = 0; c < strlen (mode_str); c++)
433       mode_str[c] = toupper (mode_str[c]);
434     if (0 == strcmp (mode_str, "PROPORTIONAL"))
435       ats_mode = MODE_PROPORTIONAL;
436     else if (0 == strcmp (mode_str, "MLP"))
437     {
438       ats_mode = MODE_MLP;
439 #if !HAVE_LIBGLPK
440       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
441                  "Assignment method `%s' configured, but GLPK is not available, please install \n",
442                  mode_str);
443       ats_mode = MODE_PROPORTIONAL;
444 #endif
445     }
446     else if (0 == strcmp (mode_str, "RIL"))
447       ats_mode = MODE_RIL;
448     else
449     {
450       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
451                  "Invalid resource assignment method `%s' configured, using proportional approach\n",
452                  mode_str);
453       ats_mode = MODE_PROPORTIONAL;
454     }
455     GNUNET_free(mode_str);
456   }
457
458   load_quotas (cfg,
459                quotas_out,
460                quotas_in,
461                GNUNET_ATS_NetworkTypeCount);
462   env.cls = NULL;
463   env.info_cb = &solver_info_cb;
464   env.bandwidth_changed_cb = &bandwidth_changed_cb;
465   env.get_preferences = &GAS_normalization_get_preferences_by_peer;
466   env.get_property = &GAS_normalization_get_properties;
467   env.cfg = cfg;
468   env.stats = GSA_stats;
469   env.addresses = GSA_addresses;
470   env.network_count = GNUNET_ATS_NetworkTypeCount;
471   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
472   {
473     env.out_quota[c] = quotas_out[c];
474     env.in_quota[c] = quotas_in[c];
475   }
476
477   switch (ats_mode) {
478     case MODE_PROPORTIONAL:
479       plugin_short = "proportional";
480       break;
481     case MODE_MLP:
482       plugin_short = "mlp";
483       break;
484     case MODE_RIL:
485       plugin_short = "ril";
486       break;
487     default:
488       plugin_short = NULL;
489       break;
490   }
491   GNUNET_asprintf (&plugin,
492                    "libgnunet_plugin_ats_%s",
493                    plugin_short);
494   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495               "Initializing solver `%s '`%s'\n",
496               plugin_short,
497               plugin);
498   if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env)))
499   {
500     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501                 _("Failed to initialize solver `%s'!\n"),
502                 plugin);
503     return GNUNET_SYSERR;
504   }
505   return GNUNET_OK;
506 }
507
508
509 /**
510  * Shutdown address subsystem.
511  */
512 void
513 GAS_plugins_done ()
514 {
515   GNUNET_PLUGIN_unload (plugin,
516                         sf);
517   sf = NULL;
518   GNUNET_free (plugin);
519   plugin = NULL;
520 }
521
522
523 /**
524  * Tell the solver that the given address can now be used
525  * for talking to the respective peer.
526  *
527  * @param new_address the new address
528  * @param addr_net network scope the address is in
529  * @param atsi performance data for the address
530  * @param atsi_count size of the @a atsi array
531  */
532 void
533 GAS_plugin_new_address (struct ATS_Address *new_address,
534                         enum GNUNET_ATS_Network_Type addr_net,
535                         const struct GNUNET_ATS_Information *atsi,
536                         uint32_t atsi_count)
537 {
538   sf->s_add (sf->cls, new_address, addr_net);
539   sf->s_bulk_start (sf->cls);
540   GAS_normalization_normalize_property (new_address,
541                                         atsi,
542                                         atsi_count);
543   sf->s_bulk_stop (sf->cls);
544 }
545
546
547 /**
548  * Tell the solver that the given address is no longer valid
549  * can cannot be used any longer.
550  *
551  * @param address address that was deleted
552  */
553 void
554 GAS_plugin_delete_address (struct ATS_Address *address)
555 {
556   sf->s_del (sf->cls, address, GNUNET_NO);
557 }
558
559
560 /**
561  * Tell the solver that the given client has expressed its
562  * appreciation for the past performance of a given connection.
563  *
564  * @param application client providing the feedback
565  * @param peer peer the feedback is about
566  * @param scope timeframe the feedback applies to
567  * @param kind performance property the feedback relates to
568  * @param score_abs degree of the appreciation
569  */
570 void
571 GAS_plugin_preference_feedback (struct GNUNET_SERVER_Client *application,
572                                 const struct GNUNET_PeerIdentity *peer,
573                                 const struct GNUNET_TIME_Relative scope,
574                                 enum GNUNET_ATS_PreferenceKind kind,
575                                 float score_abs)
576 {
577   sf->s_feedback (sf->cls,
578                   application,
579                   peer,
580                   scope,
581                   kind,
582                   score_abs);
583 }
584
585
586 /**
587  * Stop instant solving, there are many state updates
588  * happening in bulk right now.
589  */
590 void
591 GAS_plugin_solver_lock ()
592 {
593   sf->s_bulk_start (sf->cls);
594 }
595
596
597 /**
598  * Resume instant solving, we are done with the bulk state updates.
599  */
600 void
601 GAS_plugin_solver_unlock ()
602 {
603   sf->s_bulk_start (sf->cls);
604 }
605
606
607 /**
608  * Notify the plugin that a request to connect to
609  * a particular peer was given to us.
610  *
611  * @param pid identity of peer we now care about
612  */
613 void
614 GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid)
615 {
616   const struct ATS_Address *aa;
617
618   aa = sf->s_get (sf->cls, pid);
619   if (NULL == aa)
620   {
621     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
622                 "Cannot suggest address for peer `%s'\n",
623                 GNUNET_i2s (pid));
624     return;
625   }
626   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
627              "Suggesting address %p for peer `%s'\n",
628              aa,
629              GNUNET_i2s (pid));
630   GAS_scheduling_transmit_address_suggestion (pid,
631                                               aa->session_id,
632                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
633                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
634 }
635
636
637 /**
638  * Notify the plugin that a request to connect to
639  * a particular peer was dropped.
640  *
641  * @param pid identity of peer we care now less about
642  */
643 void
644 GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid)
645 {
646   sf->s_get_stop (sf->cls, pid);
647 }
648
649
650 /* end of gnunet-service-ats_plugins.c */