diff mbox

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

Message ID 1459227370-111346-2-git-send-email-dball@vmware.com
State Changes Requested
Headers show

Commit Message

Darrell Ball March 29, 2016, 4:56 a.m. UTC
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@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(-)

Comments

Russell Bryant March 29, 2016, 6:18 p.m. UTC | #1
On Tue, Mar 29, 2016 at 12:56 AM, Darrell Ball <dlu998@gmail.com> wrote:

> 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@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",
>

The patch will have to be rebased as this has changed in the meantime.


>      "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"}}},
>

The code and documentation both seem to imply that there should be at most
1 Physical_Endpoint per Port_Binding.  Should "unlimited" by "1" here?  If
multiple endpoints gains a meaning in the future, it can be changed.

More broadly, I'm also wondering if having Physical_Endpoints on each
Port_Binding makes sense.  We already have Chassis.  To me, binding the
gateway port to a chassis is the relationship that makes sense here.  How
that is realized on the chassis is a separate thing.  It seems like a list
of physical endpoints could be on the Chassis, instead.

We also have a somewhat conflicting approach in configuring each chassis.
We have ovn-bridge-mappings, but these patches take a different approach
that doesn't appear to actually work in all cases.

A Physical_Endpoint has a "chassis_port".  The code will look for
br-<chassis_port> and create it if it doesn't exist.  If ovn-controller
creates it, it won't do anything useful as no interface has been added to
the bridge.  I'd rather just make use of ovn-bridge-mappings.  Roughly, I
think that means replacing "chassis_port" with "network_name" and using
that name to figure out which bridge to use on the chassis based on its
local ovn-bridge-mappings configuration.

To be even more difficult, I still find myself wondering if this
Physical_Endpoint stuff is needed at all.  It doesn't seem to actually add
any new capabilities, but it does add new complexity.  I feel like we could
add this initial L2 gateway support without having it at all.  I couldn't
help myself and have started writing it to try to see what that would look
like.  I'm trying to do it as quick as I can so that I can get on board
with this or have a more clear alternative proposal.
Darrell Ball March 30, 2016, 1:56 a.m. UTC | #2
>
> please see responses inline
>
> ---------- Forwarded message ----------
> From: Russell Bryant <russell@ovn.org>
> Date: Tue, Mar 29, 2016 at 11:18 AM
> Subject: Re: [ovs-dev] [PATCH 1/2] [Patch V3] OVN: Physical Endpoints
> To: Darrell Ball <dlu998@gmail.com>
> Cc: ovs dev <dev@openvswitch.org>
>
>
>
>
> On Tue, Mar 29, 2016 at 12:56 AM, Darrell Ball <dlu998@gmail.com> wrote:
>
>> 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@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",
>>
>
> The patch will have to be rebased as this has changed in the meantime.
>
>
>>      "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"}}},
>>
>
> The code and documentation both seem to imply that there should be at most
> 1 Physical_Endpoint per Port_Binding.  Should "unlimited" by "1" here?  If
> multiple endpoints gains a meaning in the future, it can be changed.
>
> >> The port binding code and documentation clearly support binding
> multiple physical endpoints to a logical
> >> port.
> >> look here: *cmd_lport_bind_phys_endpts*(*struct* ctl_context *ctx)
>
>
> More broadly, I'm also wondering if having Physical_Endpoints on each
> Port_Binding makes sense.  We already have Chassis.  To me, binding the
> gateway port to a chassis is the relationship that makes sense here.  How
> that is realized on the chassis is a separate thing.  It seems like a list
> of physical endpoints could be on the Chassis, instead.
>
>
> >> The pre-existing code used by hypervisors binds logical ports to
> "physical endpts". This is done thru. the
> >> iface-id in external_ids. This is needed to map b/w logical and
> physical "endpoints".
>
>
> We also have a somewhat conflicting approach in configuring each chassis.
> We have ovn-bridge-mappings, but these patches take a different approach
> that doesn't appear to actually work in all cases.
>
> >> This approach works in all cases
> >> If there is a case that it does not support, I can comment on the
> design of that feature - let me know
>
> A Physical_Endpoint has a "chassis_port".  The code will look for
> br-<chassis_port> and create it if it doesn't exist.  If ovn-controller
> creates it, it won't do anything useful as no interface has been added to
> the bridge.
>
> >> It creates a trunk port which is all that is needed; this port is
> intended to be unmanaged by OVN
>
>  I'd rather just make use of ovn-bridge-mappings.  Roughly, I think that
> means replacing "chassis_port" with "network_name" and using that name to
> figure out which bridge to use on the chassis based on its local
> ovn-bridge-mappings configuration.
>
> >> I disagree; the bridge mappings is being generated by a human parsing
> the network topology and port
> >> mapping per chassis on every chassis; this is both error prone and
> manual work intensive.
> >> I think a later patch proposes then copying bridge-mapping to the
> chassis table so it can be used
> >> by the CMS
> >> This marshalling cannot be managed easily and is limited in
> applicability in real networks
>
> >> The physical endpoint approach has a few aspects including making the
> physical network config more
> >> plug and play, allowing the physical and logical networks to be managed
> separately and supporting
>


> >> different >>  encapsulations seen in most real provider networks
>
>
>
> To be even more difficult, I still find myself wondering if this
> Physical_Endpoint stuff is needed at all.  It doesn't seem to actually add
> any new capabilities, but it does add new complexity.  I feel like we could
> add this initial L2 gateway support without having it at all.  I couldn't
> help myself and have started writing it to try to see what that would look
> like.  I'm trying to do it as quick as I can so that I can get on board
> with this or have a more clear alternative proposal.
>
> --
> Russell Bryant
>
>
diff mbox

Patch

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},