From 9e0ef5395bbe6d17e4836495ff66103892e4a8fe Mon Sep 17 00:00:00 2001
From: "Alexander V. Chernikov" <melifaro@ipfw.ru>
Date: Sun, 15 Feb 2015 14:13:55 +0300
Subject: [PATCH 7/8] Add api for dynamic protocol creation based on dynamic
 templates.

---
 conf/cf-lex.l   |   2 +
 conf/conf.h     |   1 +
 nest/config.Y   |   4 +-
 nest/proto.c    | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 nest/protocol.h |   6 ++-
 5 files changed, 156 insertions(+), 3 deletions(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index b995e10..dfcee5f 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -657,6 +657,8 @@ cf_symbol_class_name(struct symbol *sym)
       return "protocol";
     case SYM_TEMPLATE:
       return "protocol template";
+    case SYM_DYNTEMPLATE:
+      return "dynamic template";
     case SYM_FUNCTION:
       return "function";
     case SYM_FILTER:
diff --git a/conf/conf.h b/conf/conf.h
index 71da2a1..9490377 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -124,6 +124,7 @@ struct symbol {
 #define SYM_FILTER 4
 #define SYM_TABLE 5
 #define SYM_ROA 6
+#define SYM_DYNTEMPLATE 7
 
 #define SYM_VARIABLE 0x100	/* 0x100-0x1ff are variable types */
 #define SYM_CONSTANT 0x200	/* 0x200-0x2ff are variable types */
diff --git a/nest/config.Y b/nest/config.Y
index 481d9f5..095a8fe 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -63,7 +63,7 @@ CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFA
 CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE, ROA)
 CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
 CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
-CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
+CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS, DYNAMIC)
 
 CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
 	RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -171,6 +171,7 @@ CF_ADDTO(conf, proto)
 proto_start:
    PROTOCOL { $$ = SYM_PROTO; }
  | TEMPLATE { $$ = SYM_TEMPLATE; }
+ | DYNAMIC TEMPLATE { $$ = SYM_DYNTEMPLATE; }
  ;
 
 proto_name:
@@ -191,6 +192,7 @@ proto_name:
      this_proto->name = $1->name;
 
      proto_copy_config(this_proto, $3->def);
+     this_proto->class = $1->class;
    }
  ;
 
diff --git a/nest/proto.c b/nest/proto.c
index 846d172..dda438c 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -398,6 +398,146 @@ proto_init(struct proto_config *c)
   return q;
 }
 
+/**
+ * config_create_dynamic - Creates dynamic protocol configuration
+ * @tc = config template to use
+ * @pname - instance name
+ * @p - existing protocol instance, if reconfiguring
+ * @pi - pointer to protocol-specific data
+ *
+ * Function creates all attributes for protocol - symbol of
+ * appropriate type, protocol config and performs necessary bindings.
+ *
+ * Returns pointer to protocol config or NULL.
+ */
+static struct proto_config *
+config_create_dynamic(struct proto_config *tc, char *pname, struct proto *p, void *pi)
+{
+  struct proto_config *c;
+  struct protocol *q;
+  struct symbol *sym;
+
+  q = tc->protocol;
+
+  if (!q->copy_dynconfig)
+    return NULL;
+
+  config_start_edit(tc->global);
+
+  sym = cf_find_symbol(tc->global, pname);
+  if (!sym || sym->class != SYM_VOID)
+    {
+      config_stop_edit();
+      log(L_ERR "Unable to create symbol %s for dynamic %s protocol", pname, q->name);
+      return NULL;
+    }
+  sym->class = SYM_PROTO;
+
+  c = proto_create_config(tc->global, q, SYM_PROTO);
+  proto_copy_config(c, tc);
+  sym->def = c;
+  c->name = sym->name;
+  c->dyn_parent = tc;
+  q->copy_dynconfig(c, p, pi);
+
+  config_stop_edit();
+
+  return c;
+}
+
+/**
+ * proto_create_dynamic - Creates dynamic protocol instance
+ * @tc = config template to use
+ * @pname - instance name
+ * @pi - pointer to protocol-specific data
+ *
+ * Function creates all atrributes for protocol - symbol of
+ * appropriate type, protocol config and protocol itself.
+ * Protocol is started after this.
+ *
+ * Returns pointer to protocol instance or NULL.
+ */
+struct proto *
+proto_create_dynamic(struct proto_config *tc, char *pname, void *pi)
+{
+  struct proto_config *c;
+  struct proto *p = NULL;
+  struct protocol *q;
+
+  q = tc->protocol;
+
+  if (!q->copy_dynconfig)
+    return NULL;
+
+  c = config_create_dynamic(tc, pname, NULL, pi);
+  if (!c)
+    {
+      log(L_ERR "Creating dynamic %s config for %s failed", q->name, pname);
+      return NULL;
+    }
+
+  p = proto_init(c);
+
+  if (!p)
+    {
+      log(L_ERR "Creating dynamic %s protocol instance %s failed", q->name, pname);
+      return NULL;
+    }
+
+  /* Start protocol */
+  proto_rethink_goal(p);
+
+  return p;
+}
+
+/**
+ * proto_reconfigure_dynamic - creates dynamic protocol configuration on request
+ * @p - protocol instance
+ *
+ * Function checks is the protocol is really dynamic and it dynamic
+ * template still exists in new config. If true, it checks if the original
+ * creation conditions still holds by running @retest_dynconfig callback.
+ * At the end, new configuration is created based on new template and running
+ * protocol data.
+ *
+ * Returns pointer to proto_config or NULL,
+ */
+static struct proto_config *
+proto_reconfigure_dynamic(struct config *new, struct proto *p)
+{
+  struct proto_config *oc, *nc;
+  struct symbol *sym;
+
+  DBG("Dynamic protocol %s reconfiguration check\n", p->name);
+  /* Try to find parent dynamic template in new config */
+  oc = p->cf->dyn_parent;
+  sym = cf_find_symbol(new, oc->name);
+  if (!sym || sym->class != SYM_DYNTEMPLATE)
+    {
+      DBG("Wrong dyntemplate symbol %s class: %d\n", oc->name, sym ? sym->class : 0);
+      return NULL;
+    }
+  /* Found parent configuration */
+  nc = sym->def;
+
+  /* Check if dynamic template protocol is the same */
+  if (oc->protocol != nc->protocol)
+    {
+      DBG("Wrong dyntemplate protocol: %s vs %s\n", oc->protocol->name, nc->protocol->name);
+      return NULL;
+    }
+
+  /* Check if condition still holds */
+  if (!nc->protocol->retest_dynconfig(p, nc))
+    {
+      DBG("retest dynconfig failed\n");
+      return NULL;
+    }
+
+  DBG("Start of dynamic protocol %s reconfiguration\n", p->name);
+  return config_create_dynamic(nc, p->name, p, NULL);
+}
+
 int proto_reconfig_type;  /* Hack to propagate type info to pipe reconfigure hook */
 
 static int
@@ -521,13 +661,17 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
     {
       WALK_LIST(oc, old->protos)
 	{
+	  nc = NULL;
 	  p = oc->proto;
 	  sym = cf_find_symbol(new, oc->name);
 	  if (sym && sym->class == SYM_PROTO && !new->shutdown)
+	    nc = sym->def;
+	  if (!nc && oc->dyn_parent)
+	    nc = proto_reconfigure_dynamic(new, p);
+	  if (nc)
 	    {
 	      /* Found match, let's check if we can smoothly switch to new configuration */
 	      /* No need to check description */
-	      nc = sym->def;
 	      nc->proto = p;
 
 	      /* We will try to reconfigure protocol p */
diff --git a/nest/protocol.h b/nest/protocol.h
index a20b053..0af8757 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -57,6 +57,8 @@ struct protocol {
   int (*get_attr)(struct eattr *, byte *buf, int buflen);	/* ASCIIfy dynamic attribute (returns GA_*) */
   void (*show_proto_info)(struct proto *);	/* Show protocol info (for `show protocols all' command) */
   void (*copy_config)(struct proto_config *, struct proto_config *);	/* Copy config from given protocol instance */
+  void (*copy_dynconfig)(struct proto_config *, struct proto *, void *);    /* Clone config from given dynamic template */
+  int (*retest_dynconfig)(struct proto *, struct proto_config *);
 };
 
 void protos_build(void);
@@ -65,6 +67,7 @@ void protos_preconfig(struct config *);
 void protos_postconfig(struct config *);
 void protos_commit(struct config *new, struct config *old, int force_restart, int type);
 void protos_dump_all(void);
+struct proto *proto_create_dynamic(struct proto_config *tc, char *pname, void *pi);
 
 #define GA_UNKNOWN	0		/* Attribute not recognized */
 #define GA_NAME		1		/* Result = name */
@@ -89,7 +92,7 @@ struct proto_config {
   struct proto *proto;			/* Instance we've created */
   char *name;
   char *dsc;
-  int class;				/* SYM_PROTO or SYM_TEMPLATE */
+  int class;				/* SYM_PROTO or SYM_TEMPLATE or SYM_DYNTEMPLATE */
   u32 debug, mrtdump;			/* Debugging bitfields, both use D_* constants */
   unsigned preference, disabled;	/* Generic parameters */
   int in_keep_filtered;			/* Routes rejected in import filter are kept */
@@ -100,6 +103,7 @@ struct proto_config {
 					   (relevant when in_keep_filtered is active) */
   struct proto_limit *in_limit;		/* Limit for importing routes from protocol */
   struct proto_limit *out_limit;	/* Limit for exporting routes to protocol */
+  struct proto_config *dyn_parent;	/* Parent dynamic template */
 
   /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
 
-- 
2.1.2

