[ovs-dev] [PATCH 1/2] [Patch V3] OVN: Physical Endpoints

Darrell Ball dlu998 at gmail.com
Tue Mar 29 04:56:09 UTC 2016


The following patch series implements physical-logical separation
to be used presently by gateways.

The physical endpoint changes allow the physical network to be
managed more easily by a dedicated provider network management function
and also allow the northbound schema to remain purely logical.
Another benefit to allow more easily for additional encapsulations to be
used when interacting with physical provider networks.

Physical endpoints are defined here as endpoints at the border of a physical network.
This presently applies to gateways.
If no physical endpt, the encap is assumed empty.
For gateways, a single physical endpoint must be bound to each logical port.
The chassis name must match the chassis system-id. All 6 arguments must
be supplied for gateway physical endpoints configuration.
Support is provided to bind multiple physical endpoints to a logical port for
future considerations.

Physical Endpoint Patch:

ovn-sb.ovsschema: Add physical endpoint table and phys_endpts to logical ports

ovn-sb.xml: Update documentation regarding physical endpoints and their
binding to logical ports. Also describe possible future encapsulation type usages.

ovn-sbctl.c: Add configuration for physcial endpoints and binding to logical ports.

ovn-sbctl.8.in: Update the ovn-sbctl man page for physical endpoints and binding
to logical ports.

Signed-off-by: Darrell Ball <dball at vmware.com>
---
 ovn/ovn-sb.ovsschema         |  23 +++-
 ovn/ovn-sb.xml               |  75 +++++++++++
 ovn/utilities/ovn-sbctl.8.in |  47 +++++++
 ovn/utilities/ovn-sbctl.c    | 290 ++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 429 insertions(+), 6 deletions(-)

diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema
index 32f8748..14ecb99 100644
--- a/ovn/ovn-sb.ovsschema
+++ b/ovn/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "1.2.0",
-    "cksum": "1259182303 5368",
+    "version": "1.3.0",
+    "cksum": "4176169852 6394",
     "tables": {
         "Chassis": {
             "columns": {
@@ -71,6 +71,21 @@
                              "min": 0, "max": "unlimited"}}},
             "indexes": [["tunnel_key"]],
             "isRoot": true},
+        "Physical_Endpoint": {
+            "columns": {
+                "name": {"type": "string"},
+                "chassis": {"type": {"key": {"type": "uuid",
+                                             "refTable": "Chassis",
+                                             "refType": "weak"},
+                                     "min": 1, "max": 1}},
+                "chassis_port": {"type": {"key": "string", "min": 1, "max": 1}},
+                "type": {"type": {"key": {
+                           "type": "string",
+                           "enum": ["set", ["vlan"]]}}},
+                "ingress_encap": {"type": "string"},
+                "egress_encap": {"type": "string"}},
+            "indexes": [["name"]],
+            "isRoot": true},
         "Port_Binding": {
             "columns": {
                 "logical_port": {"type": "string"},
@@ -96,6 +111,10 @@
                                              "refTable": "Chassis",
                                              "refType": "weak"},
                                      "min": 0, "max": 1}},
+                "phys_endpts": {"type": {"key": {"type": "uuid",
+                                             "refTable": "Physical_Endpoint",
+                                             "refType": "weak"},
+                                     "min": 0, "max": "unlimited"}},
                 "mac": {"type": {"key": "string",
                                  "min": 0,
                                  "max": "unlimited"}}},
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index effedb0..af80d0e 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1178,6 +1178,74 @@ tcp.flags = RST;
     </group>
   </table>
 
+  <table name="Physical_Endpoint" title="Endpoints on physical networks ">
+   <p>
+      Each row in this table identifies an endpoint on a physical access
+      network.  The <ref table="Port_Binding"/> table references physical
+      endpoints for gateways. A physical endpoint comprises both a
+      location on a chassis port and encapsulation for access. A given
+      chassis port location can have several encapsulations and therefore
+      physical endpoints.
+    </p>
+
+    <p>
+      If multiple encapsulations are needed, multiple physical endpoints
+      would be defined and bound to separate logical ports.
+      If no physical endpt is bound, encap is assumed empty.
+    </p>
+
+    <p>
+      For gateways, a single physical endpoint must be bound to each
+      logical port. The chassis name of the physical endpoint must
+      match the chassis system-id.
+    </p>
+
+    <column name="name">
+      Unique name to reference the physical endpoint.
+    </column>
+
+    <column name="chassis">
+      A transport node bordering on a physical provider network;
+      gateways are an example. SW gateways are presently supported.
+    </column>
+
+    <column name="chassis_port">
+      A port, such as eth0, on a transport node that borders
+      on a physical provider network. Use case is gateways.
+    </column>
+
+    <column name="type">
+      The encapsulation type of the physical endpoint. Types include
+      a single vlan. This could later be extended to include mpls
+      labels, IP tunnel outer header and dual vlans. Note that
+      encapsulations are often directionally different, meaning
+      the encapsulation expected on ingress is different from the
+      encapsulation for egressing packets; mpls labels and IP tunnels
+      being two examples.
+      Single mpls label support would be later defined with default
+      values of 0 for exp, 1 for BOS and 64 for TTL. Default would
+      also imply downstream label assigned with ethertype of 0x8847.
+      An example would be:
+      type: mpls_label_def
+      egress_encap: 1023
+      ingress_encap: 667
+      which means a mpls label with 667 is expected on ingress and
+      a label with 1023 is used on egress. mpls_label_def implies
+      no vlan encapsulation in addition to the mpls. This behavior
+      can be further extended with a vlan being present in addition
+      to an mpls label.
+    </column>
+
+    <column name="ingress_encap">
+      The ingress encapsulation type of the physical endpoint.
+    </column>
+
+    <column name="egress_encap">
+      The egress encapsulation type of the physical endpoint.
+    </column>
+
+  </table>
+
   <table name="Port_Binding" title="Physical-Logical Port Bindings">
     <p>
       Most rows in this table identify the physical location of a logical port.
@@ -1232,6 +1300,13 @@ tcp.flags = RST;
         <code>ovn-controller</code>/<code>ovn-controller-vtep</code>.
       </column>
 
+      <column name="phys_endpts">
+        The physical endpoints that the logical port resides on.
+        To successfully identify phys_endpts, this column must be
+        <ref table="Physical_Endpoint"/> records.  This is populated by a
+        <code>CMS</code> physical management component.
+      </column>
+
       <column name="tunnel_key">
         <p>
           A number that represents the logical port in the key (e.g. STT key or
diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in
index e4e4431..43c9b61 100644
--- a/ovn/utilities/ovn-sbctl.8.in
+++ b/ovn/utilities/ovn-sbctl.8.in
@@ -131,6 +131,29 @@ Without \fB\-\-if\-exists\fR, attempting to delete a chassis that does
 not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
 delete a chassis that does not exist has no effect.
 .
+.SS "Physical Endpoint Commands"
+These commands manipulate \fBOVN_Southbound\fR physical endpoint.
+.
+.IP "[\fB\-\-may\-exist\fR] \fBphys\-endpt\-add \fIphys\-endpt\fR \fIchassis\fR \fIchassis\-port\fR \fIencap\-type\fR \fIingress\-encap\fR \fIegress\-encap\fR"
+Creates a new physical endpoint named \fIphys\-endpt\fR.  \fIchassis\fR is a
+chassis name on a physical network edge, such as a gateway.
+\fIchassis\-port\fR is a chassis port on a physical edge.
+\fIencap\-type\fR is a type of encap such as vlan, double vlan, mpls or one
+of several IP tunnel encaps. \fIingress\-encap\fR is the
+ingress encap value. \fIegress\-encap\fR is the egress encap value.
+Both encap values may be the same in some cases.
+.IP
+Without \fB\-\-may\-exist\fR, attempting to create a phys-endpt
+that exists is an error.  With \fB\-\-may\-exist\fR, this command does
+nothing if phys-endpt already exists.
+.
+.IP "[\fB\-\-if\-exists\fR] \fBphys\-endpt\-del \fIphys\-endpt\fR"
+Deletes \fIphys\-endpt\fR.
+.IP
+Without \fB\-\-if\-exists\fR, attempting to delete a phys-endpt that does
+not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
+delete a phys-endpt that does not exist has no effect.
+.
 .SS "Port binding Commands"
 .
 These commands manipulate \fBOVN_Southbound\fR port bindings.
@@ -150,6 +173,30 @@ Without \fB\-\-if\-exists\fR, attempting to unbind a logical port
 that is not bound is an error.  With \fB\-\-if\-exists\fR, attempting
 to unbind logical port that is not bound has no effect.
 .
+.SS "Port binding to physical endpoint Commands"
+.
+These commands manipulate \fBOVN_Southbound\fR port binding to physical endpoints.
+.
+.IP "[\fB\-\-may\-exist\fR] \fBlport\-bind\-phys\-endpt \fIlogical\-port\fR \fIphys\-endpt\fR"
+Binds the logical port named \fIlogical\-port\fR to \fIphys\-endpts\fR, which
+is a common separated list of physical endpoints. If no physical
+endpoint is bound, the encap is empty. Multiple phys_endpts bound to a
+logical port may have applications in future and are therefore provided.
+Gateway logical ports should be bound to one physical endpoint for
+the gateway to function properly. All physical endpoints must be
+supplied at binding presently.
+.IP
+Without \fB\-\-may\-exist\fR, attempting to bind a logical port that
+has already been bound is an error.  With \fB\-\-may\-exist\fR, this
+command does nothing if \fIlogical\-port\fR has already been bound.
+.
+.IP "[\fB\-\-if\-exists\fR] \fBlport\-unbind\-phys\-endpt\fR \fIlogical\-port\fR"
+Resets the binding of \fIlogical\-port\fR to \fINULL\fR.
+.IP
+Without \fB\-\-if\-exists\fR, attempting to unbind a logical port
+that is not bound is an error.  With \fB\-\-if\-exists\fR, attempting
+to unbind logical port that is not bound has no effect.
+.
 .SS "Logical Flow Commands"
 .
 .IP "\fBlflow\-list\fR [\fIlogical\-datapath\fR]"
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index a257729..bb360d5 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -312,11 +312,25 @@ Chassis commands:\n\
                                            and ENCAP-IP\n\
   chassis-del CHASSIS         delete CHASSIS and all of its encaps\n\
                               and gateway_ports\n\
+Physical Endpoint commands:\n\
+  phys-endpt-add PHYS-ENDPT CHASSIS PORT TYPE ING-ENC EGR-ENC\n\
+                                 create a new phys endpt named\n\
+                                 PHYS-ENDPT on CHASSIS/PORT\n\
+                                 with TYPE encap\n\
+                                 and encap values ING-ENC EGR-ENC\n\
+                                 chassis and port may be omitted \n\
+  phys-endpt-del PHYS-ENDPT      delete PHYS-ENDPT \n\
 \n\
 Port binding commands:\n\
   lport-bind LPORT CHASSIS    bind logical port LPORT to CHASSIS\n\
   lport-unbind LPORT          reset the port binding of logical port LPORT\n\
 \n\
+Port binding Phys Endpt commands:\n\
+  lport-bind-phys-endpt LPORT PHYS-ENDPT\n\
+                             bind logical port LPORT to PHYS-ENDPT\n\
+  lport-unbind-phys-endpt LPORT\n\
+                          reset the binding of LPORT to PHYS-ENDPT\n\
+\n\
 Logical flow commands:\n\
   lflow-list [DATAPATH]       List logical flows for all or a single datapath\n\
   dump-flows [DATAPATH]       Alias for lflow-list\n\
@@ -358,6 +372,9 @@ struct sbctl_context {
     struct shash chassis;
     /* Maps from lport name to struct sbctl_port_binding. */
     struct shash port_bindings;
+
+    /* Maps from phys_endpt name to struct sbctl_physical_endpoint. */
+    struct shash phys_endpts_name_to_rec;
 };
 
 /* Casts 'base' into 'struct sbctl_context'. */
@@ -375,6 +392,10 @@ struct sbctl_port_binding {
     const struct sbrec_port_binding *bd_cfg;
 };
 
+struct sbctl_physical_endpoint {
+    const struct sbrec_physical_endpoint *phys_endpt_db_rec;
+};
+
 static void
 sbctl_context_invalidate_cache(struct ctl_context *ctx)
 {
@@ -383,9 +404,10 @@ sbctl_context_invalidate_cache(struct ctl_context *ctx)
     if (!sbctl_ctx->cache_valid) {
         return;
     }
-    sbctl_ctx->cache_valid = false;
     shash_destroy_free_data(&sbctl_ctx->chassis);
     shash_destroy_free_data(&sbctl_ctx->port_bindings);
+    shash_destroy_free_data(&sbctl_ctx->phys_endpts_name_to_rec);
+    sbctl_ctx->cache_valid = false;
 }
 
 static void
@@ -394,7 +416,8 @@ sbctl_context_populate_cache(struct ctl_context *ctx)
     struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
     const struct sbrec_chassis *chassis_rec;
     const struct sbrec_port_binding *port_binding_rec;
-    struct sset chassis, port_bindings;
+    const struct sbrec_physical_endpoint *phys_endpt_db_rec;
+    struct sset chassis, port_bindings, physical_endpoints_set;
 
     if (sbctl_ctx->cache_valid) {
         /* Cache is already populated. */
@@ -403,6 +426,8 @@ sbctl_context_populate_cache(struct ctl_context *ctx)
     sbctl_ctx->cache_valid = true;
     shash_init(&sbctl_ctx->chassis);
     shash_init(&sbctl_ctx->port_bindings);
+    shash_init(&sbctl_ctx->phys_endpts_name_to_rec);
+
     sset_init(&chassis);
     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) {
         struct sbctl_chassis *ch;
@@ -436,10 +461,30 @@ sbctl_context_populate_cache(struct ctl_context *ctx)
                   bd);
     }
     sset_destroy(&port_bindings);
+
+    sset_init(&physical_endpoints_set);
+    SBREC_PHYSICAL_ENDPOINT_FOR_EACH(phys_endpt_db_rec, ctx->idl) {
+        struct sbctl_physical_endpoint *phys_endpt_lrec;
+
+        if (!sset_add(&physical_endpoints_set, phys_endpt_db_rec->name)) {
+            VLOG_WARN("database contains duplicate physical endpoint record "
+                      "for name (%s)",
+					  phys_endpt_db_rec->name);
+            continue;
+        }
+
+        phys_endpt_lrec
+		     = xmalloc(sizeof *phys_endpt_lrec);
+        phys_endpt_lrec->phys_endpt_db_rec = phys_endpt_db_rec;
+        shash_add(&sbctl_ctx->phys_endpts_name_to_rec,
+                  phys_endpt_db_rec->name, phys_endpt_lrec);
+    }
+    sset_destroy(&physical_endpoints_set);
+
 }
 
 static void
-check_conflicts(struct sbctl_context *sbctl_ctx, const char *name,
+chassis_check_conflict(struct sbctl_context *sbctl_ctx, const char *name,
                 char *msg)
 {
     if (shash_find(&sbctl_ctx->chassis, name)) {
@@ -449,6 +494,18 @@ check_conflicts(struct sbctl_context *sbctl_ctx, const char *name,
     free(msg);
 }
 
+static void
+physical_endpoint_check_conflict(
+          struct sbctl_context *sbctl_ctx, const char *name,
+          char *msg)
+{
+    if (shash_find(&sbctl_ctx->phys_endpts_name_to_rec, name)) {
+        ctl_fatal("%s because a physical endpoint named %s already exists",
+                    msg, name);
+    }
+    free(msg);
+}
+
 static struct sbctl_chassis *
 find_chassis(struct sbctl_context *sbctl_ctx, const char *name,
              bool must_exist)
@@ -481,6 +538,23 @@ find_port_binding(struct sbctl_context *sbctl_ctx, const char *name,
     return bd;
 }
 
+static struct sbctl_physical_endpoint *
+find_phys_endpt(struct sbctl_context *sbctl_ctx, const char *name,
+                bool must_exist)
+{
+    struct sbctl_physical_endpoint *phys_endpt_lrec;
+
+    ovs_assert(sbctl_ctx->cache_valid);
+
+    phys_endpt_lrec =
+        shash_find_data(&sbctl_ctx->phys_endpts_name_to_rec, name);
+    if (must_exist && !phys_endpt_lrec) {
+        ctl_fatal("no physical endpoint named %s", name);
+    }
+
+    return phys_endpt_lrec;
+}
+
 static void
 pre_get_info(struct ctl_context *ctx)
 {
@@ -490,8 +564,16 @@ pre_get_info(struct ctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type);
     ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip);
 
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_name);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_chassis);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_chassis_port);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_type);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_ingress_encap);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_physical_endpoint_col_egress_encap);
+
     ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port);
     ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis);
+    ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_phys_endpts);
 
     ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath);
     ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_pipeline);
@@ -519,6 +601,13 @@ static struct cmd_show_table cmd_show_tables[] = {
       NULL},
      {NULL, NULL, NULL}},
 
+     {&sbrec_table_physical_endpoint,
+	  &sbrec_physical_endpoint_col_name,
+	  {&sbrec_physical_endpoint_col_chassis,
+	   &sbrec_physical_endpoint_col_chassis_port,
+	   &sbrec_physical_endpoint_col_ingress_encap},
+	  {NULL, NULL, NULL}},
+
     {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}},
 };
 
@@ -542,7 +631,7 @@ cmd_chassis_add(struct ctl_context *ctx)
             return;
         }
     }
-    check_conflicts(sbctl_ctx, ch_name,
+    chassis_check_conflict(sbctl_ctx, ch_name,
                     xasprintf("cannot create a chassis named %s", ch_name));
 
     char *tokstr = xstrdup(encap_types);
@@ -599,6 +688,108 @@ cmd_chassis_del(struct ctl_context *ctx)
 }
 
 static void
+cmd_phys_endpt_add(struct ctl_context *ctx)
+{
+    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+    bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+    const char *phys_endpt_names, *chassis_port_name;
+    const char *chassis_name;
+    const char *type_name, *ing_encap, *egr_encap;
+    struct sbctl_chassis *sbctl_ch;
+    int v;
+
+    if (ctx->argc == 7) {
+        phys_endpt_names = ctx->argv[1];
+        chassis_name = ctx->argv[2];
+        chassis_port_name = ctx->argv[3];
+        type_name = ctx->argv[4];
+        ing_encap = ctx->argv[5];
+        egr_encap = ctx->argv[6];
+    } else {
+        ctl_fatal("phys endpt config must have 6 args ");
+    }
+
+    sbctl_context_populate_cache(ctx);
+    if (may_exist) {
+        struct sbctl_physical_endpoint
+            *phys_endpt_lrec =
+               find_phys_endpt(
+                    sbctl_ctx, phys_endpt_names, false);
+        if (phys_endpt_lrec) {
+            return;
+        }
+    }
+    physical_endpoint_check_conflict(
+        sbctl_ctx, phys_endpt_names,
+        xasprintf("cannot create physical endpoint %s",
+                  phys_endpt_names));
+
+    /* Reminder: Splice out a encap verify function.
+       Presently only supporting single vlan with same
+       value for ingress and egress */
+    if ( (strcmp(type_name, "vlan")) ||
+        (!str_to_int(ing_encap, 10, &v) || v < 0 || v > 4095) ||
+        (!str_to_int(egr_encap, 10, &v) || v < 0 || v > 4095) ||
+        (strcmp(ing_encap, egr_encap))) {
+        ctl_fatal("phys endpt (%s) unsupported encap ",
+                  phys_endpt_names);
+    }
+
+    struct sbrec_physical_endpoint *phys_endpt_db_rec =
+       sbrec_physical_endpoint_insert(ctx->txn);
+
+    sbrec_physical_endpoint_set_name(
+            phys_endpt_db_rec,
+            phys_endpt_names);
+
+    if (chassis_name) {
+        sbctl_ch = find_chassis(sbctl_ctx, chassis_name, false);
+        if (sbctl_ch) {
+            sbrec_physical_endpoint_set_chassis(
+                phys_endpt_db_rec,
+                sbctl_ch->ch_cfg);
+        }
+    }
+    sbrec_physical_endpoint_set_chassis_port(
+            phys_endpt_db_rec,
+			chassis_port_name);
+    sbrec_physical_endpoint_set_type(
+            phys_endpt_db_rec,
+            type_name);
+    sbrec_physical_endpoint_set_ingress_encap(
+            phys_endpt_db_rec,
+			ing_encap);
+    sbrec_physical_endpoint_set_egress_encap(
+            phys_endpt_db_rec,
+            egr_encap);
+
+    sbctl_context_invalidate_cache(ctx);
+}
+
+static void
+cmd_phys_endpt_del(struct ctl_context *ctx)
+{
+    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+    bool must_exist = !shash_find(&ctx->options, "--if-exists");
+    struct sbctl_physical_endpoint *phys_endpt_lrec;
+
+    sbctl_context_populate_cache(ctx);
+    phys_endpt_lrec = find_phys_endpt(
+            sbctl_ctx, ctx->argv[1], must_exist);
+    if (phys_endpt_lrec) {
+        if (phys_endpt_lrec->phys_endpt_db_rec) {
+
+            sbrec_physical_endpoint_delete(
+                phys_endpt_lrec->phys_endpt_db_rec);
+        }
+        shash_find_and_delete(
+                     &sbctl_ctx->phys_endpts_name_to_rec,
+                     ctx->argv[1]);
+        free(phys_endpt_lrec);
+    }
+}
+
+static void
 cmd_lport_bind(struct ctl_context *ctx)
 {
     struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
@@ -643,6 +834,79 @@ cmd_lport_unbind(struct ctl_context *ctx)
     }
 }
 
+static void
+cmd_lport_bind_phys_endpts(struct ctl_context *ctx)
+{
+    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+    bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+    struct sbctl_port_binding *sbctl_bd;
+    struct sbctl_physical_endpoint *phys_endpt_lrec;
+    char *lport_name, *phys_endpt_names;
+
+
+    /* port_binding must exist, chassis must exist! */
+    lport_name = ctx->argv[1];
+    phys_endpt_names = ctx->argv[2];
+
+    sbctl_context_populate_cache(ctx);
+    sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true);
+
+    if (sbctl_bd->bd_cfg->phys_endpts) {
+        if (may_exist) {
+            return;
+        } else {
+            ctl_fatal("lport (%s) already bound to phys_endpts ",
+                      lport_name);
+        }
+    }
+
+    /* Parse the individual endpoints from the endpoints string
+     * and dump them into a set
+     */
+    char *tokstr = xstrdup(phys_endpt_names);
+    char *token, *save_ptr = NULL;
+    struct sset phys_endpt_names_set = SSET_INITIALIZER(&phys_endpt_names_set);
+    for (token = strtok_r(tokstr, ",", &save_ptr); token != NULL;
+         token = strtok_r(NULL, ",", &save_ptr)) {
+        sset_add(&phys_endpt_names_set, token);
+    }
+    free(tokstr);
+
+    size_t num_phys_endpts = sset_count(&phys_endpt_names_set);
+    struct sbrec_physical_endpoint **phys_endpts =
+        xmalloc(num_phys_endpts * sizeof *phys_endpts);
+    const char *phys_endpt_name;
+    int i = 0;
+    SSET_FOR_EACH (phys_endpt_name, &phys_endpt_names_set){
+        phys_endpt_lrec = find_phys_endpt(sbctl_ctx, phys_endpt_name, true);
+        if (phys_endpt_lrec) {
+            phys_endpts[i] = CONST_CAST(struct sbrec_physical_endpoint *,
+                                        phys_endpt_lrec->phys_endpt_db_rec);
+            i++;
+        }
+    }
+    sset_destroy(&phys_endpt_names_set);
+
+    sbrec_port_binding_set_phys_endpts(sbctl_bd->bd_cfg, phys_endpts, i);
+    sbctl_context_invalidate_cache(ctx);
+}
+
+static void
+cmd_lport_unbind_phys_endpts(struct ctl_context *ctx)
+{
+    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+    bool must_exist = !shash_find(&ctx->options, "--if-exists");
+    struct sbctl_port_binding *sbctl_bd;
+    char *lport_name;
+
+    lport_name = ctx->argv[1];
+    sbctl_context_populate_cache(ctx);
+    sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist);
+    if (sbctl_bd) {
+        sbrec_port_binding_set_phys_endpts(sbctl_bd->bd_cfg, NULL, 0);
+    }
+}
+
 enum {
     PL_INGRESS,
     PL_EGRESS,
@@ -776,6 +1040,11 @@ static const struct ctl_table_class tables[] = {
      {{&sbrec_table_mac_binding, &sbrec_mac_binding_col_logical_port, NULL},
       {NULL, NULL, NULL}}},
 
+    {&sbrec_table_physical_endpoint,
+     {{&sbrec_table_physical_endpoint,
+       &sbrec_physical_endpoint_col_name, NULL},
+      {NULL, NULL, NULL}}},
+
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 };
 
@@ -1012,17 +1281,30 @@ sbctl_exit(int status)
 
 static const struct ctl_command_syntax sbctl_commands[] = {
     /* Chassis commands. */
+
     {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info,
      cmd_chassis_add, NULL, "--may-exist", RW},
     {"chassis-del", 1, 1, "CHASSIS", pre_get_info, cmd_chassis_del, NULL,
      "--if-exists", RW},
 
+	/* Physical Endpoint commands */
+	{"phys-endpt-add", 4, 6, "PHYS-ENDPT CHASSIS PORT TYPE ING-ENC EGR-ENC",
+     pre_get_info, cmd_phys_endpt_add, NULL, "--may-exist", RW},
+	{"phys-endpt-del", 1, 1, "PHYS-ENDPT",
+     pre_get_info, cmd_phys_endpt_del, NULL, "--if-exists", RW},
+
     /* Port binding commands. */
     {"lport-bind", 2, 2, "LPORT CHASSIS", pre_get_info, cmd_lport_bind, NULL,
      "--may-exist", RW},
     {"lport-unbind", 1, 1, "LPORT", pre_get_info, cmd_lport_unbind, NULL,
      "--if-exists", RW},
 
+	/* Port to physical endpoint binding */
+    {"lport-bind-phys-endpt", 2, 2, "LPORT PHYS-ENDPT", pre_get_info,
+     cmd_lport_bind_phys_endpts, NULL, "--may-exist", RW},
+    {"lport-unbind-phys-endpt", 1, 1, "LPORT", pre_get_info,
+     cmd_lport_unbind_phys_endpts, NULL, "--if-exists", RW},
+
     /* Logical flow commands */
     {"lflow-list", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
      "", RO},
-- 
1.9.1




More information about the dev mailing list