diff mbox series

[ovs-dev,v3,17/27] ovn-northd-ddlog: Remove `ls` field from `Switch`.

Message ID 20210507040659.26830-18-blp@ovn.org
State Accepted
Headers show
Series ddlog 5x performance improvement | expand

Commit Message

Ben Pfaff May 7, 2021, 4:06 a.m. UTC
From: Leonid Ryzhyk <lryzhyk@vmware.com>

This commit is analogous to 076749c99, but switches instead of routers.

`relation Switch` stores the internal representation of a logical
switch, consisting of values from the `nb::Logical_Switch` table
augmented with some additional fields.  We used to do this by
copying the entire `Logical_Switch` record inside `Switch`.  This
proved highly inefficient in scenarios where some of the entities that
`Logical_Switch` references (logicl switch ports, ACLs, or QoS rules)
change frequently.  Every such change modifies the `Logical_Switch`
record, which triggers an update of the `Switch` object, which can cause
a bunch of rules to update their outputs.

As a workaround, we no longer store the entire `Logical_Switch` object
in the `Switch` table, and instead only copy its relevant fields.

Signed-off-by: Leonid Ryzhyk <lryzhyk@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 northd/ipam.dl       |  25 ++--
 northd/lswitch.dl    |  24 ++-
 northd/ovn_northd.dl | 342 ++++++++++++++++++++++---------------------
 3 files changed, 203 insertions(+), 188 deletions(-)

Comments

0-day Robot May 7, 2021, 5:12 a.m. UTC | #1
Bleep bloop.  Greetings Ben Pfaff, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
WARNING: Unexpected sign-offs from developers who are not authors or co-authors or committers: Ben Pfaff <blp@ovn.org>
Lines checked: 1526, Warnings: 1, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
diff mbox series

Patch

diff --git a/northd/ipam.dl b/northd/ipam.dl
index 589126f81288..e7373f250a7f 100644
--- a/northd/ipam.dl
+++ b/northd/ipam.dl
@@ -95,18 +95,17 @@  function parse_dynamic_address_request(s: string): Option<dynamic_address_reques
 relation SwitchIPv4ReservedAddress(lswitch: uuid, addr: bit<32>)
 
 /* Add reserved address groups (1) and (2). */
-SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
+SwitchIPv4ReservedAddress(.lswitch = sw._uuid,
                           .addr    = addr) :-
-    &Switch(.ls = ls,
-            .subnet = Some{(_, _, start_ipv4, total_ipv4s)}),
+    sw in &Switch(.subnet = Some{(_, _, start_ipv4, total_ipv4s)}),
     var exclude_ips = {
         var exclude_ips = set_singleton(start_ipv4);
         exclude_ips.insert(start_ipv4 + total_ipv4s - 1);
-        match (map_get(ls.other_config, "exclude_ips")) {
+        match (map_get(sw.other_config, "exclude_ips")) {
             None -> exclude_ips,
             Some{exclude_ip_list} -> match (parse_ip_list(exclude_ip_list)) {
                 Left{err} -> {
-                    warn("logical switch ${uuid2str(ls._uuid)}: bad exclude_ips (${err})");
+                    warn("logical switch ${uuid2str(sw._uuid)}: bad exclude_ips (${err})");
                     exclude_ips
                 },
                 Right{ranges} -> {
@@ -124,7 +123,7 @@  SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
                                 exclude_ips.insert(addr)
                             }
                         } else {
-                            warn("logical switch ${uuid2str(ls._uuid)}: excluded addresses not in subnet")
+                            warn("logical switch ${uuid2str(sw._uuid)}: excluded addresses not in subnet")
                         }
                     };
                     exclude_ips
@@ -135,11 +134,11 @@  SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
     var addr = FlatMap(exclude_ips).
 
 /* Add reserved address group (3). */
-SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
+SwitchIPv4ReservedAddress(.lswitch = ls_uuid,
                           .addr    = addr) :-
     SwitchPortStaticAddresses(
         .port = &SwitchPort{
-            .sw = &Switch{.ls = ls,
+            .sw = &Switch{._uuid = ls_uuid,
                           .subnet = Some{(_, _, start_ipv4, total_ipv4s)}},
             .peer = None},
         .addrs = lport_addrs
@@ -157,10 +156,10 @@  SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
     var addr = FlatMap(addrs).
 
 /* Add reserved address group (4) */
-SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
+SwitchIPv4ReservedAddress(.lswitch = ls_uuid,
                           .addr    = addr) :-
     &SwitchPort(
-            .sw = &Switch{.ls = ls,
+            .sw = &Switch{._uuid = ls_uuid,
                           .subnet = Some{(_, _, start_ipv4, total_ipv4s)}},
             .peer = Some{&rport}),
     var addrs = {
@@ -176,7 +175,7 @@  SwitchIPv4ReservedAddress(.lswitch = ls._uuid,
     var addr = FlatMap(addrs).
 
 /* Add reserved address group (5) */
-SwitchIPv4ReservedAddress(.lswitch = sw.ls._uuid,
+SwitchIPv4ReservedAddress(.lswitch = sw._uuid,
                           .addr    = ip_addr.a) :-
     &SwitchPort(.sw = &sw, .lsp = lsp, .static_dynamic_ipv4 = Some{ip_addr}).
 
@@ -199,7 +198,7 @@  SwitchPortAllocatedIPv4DynAddress(lsport, dyn_addr) :-
     /* Aggregate all ports of a switch that need a dynamic IP address */
     port in &SwitchPort(.needs_dynamic_ipv4address = true,
                         .sw = &sw),
-    var switch_id = sw.ls._uuid,
+    var switch_id = sw._uuid,
     var ports = port.group_by(switch_id).to_vec(),
     SwitchIPv4ReservedAddresses(switch_id, reserved_addrs),
     /* Allocate dynamic addresses only for ports that don't have a dynamic address
@@ -437,7 +436,7 @@  SwitchPortNewMACDynAddress(lsp._uuid, mac_addr) :-
         None -> None,
         Some{addr} -> {
             if (sw.subnet.is_some() or sw.ipv6_prefix.is_some() or
-                map_get(sw.ls.other_config, "mac_only") == Some{"true"}) {
+                map_get(sw.other_config, "mac_only") == Some{"true"}) {
                 Some{addr}
             } else {
                 None
diff --git a/northd/lswitch.dl b/northd/lswitch.dl
index 8a37810ae032..b5f53412d313 100644
--- a/northd/lswitch.dl
+++ b/northd/lswitch.dl
@@ -187,7 +187,14 @@  LogicalSwitchHasNonRouterPort(ls, false) :-
 /* Switch relation collects all attributes of a logical switch */
 
 typedef Switch = Switch {
-    ls:                nb::Logical_Switch,
+    /* Fields copied from nb::Logical_Switch_Port. */
+    _uuid:             uuid,
+    name:              string,
+    load_balancer:     Set<uuid>,
+    other_config:      Map<string,string>,
+    external_ids:      Map<string,string>,
+
+    /* Additional computed fields. */
     has_stateful_acl:  bool,
     has_lb_vip:        bool,
     has_dns_records:   bool,
@@ -217,7 +224,12 @@  function ipv6_parse_prefix(s: string): Option<in6_addr> {
 }
 
 Switch[Switch{
-           .ls                = ls,
+           ._uuid             = ls._uuid,
+           .name              = ls.name,
+           .load_balancer     = ls.load_balancer,
+           .other_config      = ls.other_config,
+           .external_ids      = ls.external_ids,
+
            .has_stateful_acl  = has_stateful_acl,
            .has_lb_vip        = has_lb_vip,
            .has_dns_records   = has_dns_records,
@@ -457,7 +469,8 @@  SwitchPortDHCPv6Options(port, options) :-
 relation SwitchQoS(sw: Intern<Switch>, qos: Ref<nb::QoS>)
 
 SwitchQoS(sw, qos) :-
-    sw in &Switch(.ls = nb::Logical_Switch{.qos_rules = qos_rules}),
+    sw in &Switch(),
+    nb::Logical_Switch(._uuid = sw._uuid, .qos_rules = qos_rules),
     var qos_rule = FlatMap(qos_rules),
     qos in &QoSRef[nb::QoS{._uuid = qos_rule}].
 
@@ -486,7 +499,7 @@  relation &SwitchACL(sw: Intern<Switch>,
 
 &SwitchACL(.sw = sw, .acl = acl, .has_fair_meter = has_fair_meter) :-
     LogicalSwitchACL(sw_uuid, acl_uuid),
-    sw in &Switch(.ls = nb::Logical_Switch{._uuid = sw_uuid}),
+    sw in &Switch(._uuid = sw_uuid),
     acl in &ACLRef[nb::ACL{._uuid = acl_uuid}],
     ACLHasFairMeter(acl, has_fair_meter).
 
@@ -581,7 +594,8 @@  relation &SwitchPort(
             .hac_group_uuid             = hac_group_uuid) :-
     nb::Logical_Switch_Port[lsp],
     LogicalSwitchPort(lsp._uuid, lswitch_uuid),
-    sw in &Switch(.ls = nb::Logical_Switch{._uuid = lswitch_uuid, .other_config = other_config},
+    sw in &Switch(._uuid = lswitch_uuid,
+                  .other_config = other_config,
                   .subnet = subnet,
                   .ipv6_prefix = ipv6_prefix),
     SwitchRouterPeerRef(lsp._uuid, peer),
diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
index 50203de88867..95de5c0c5d70 100644
--- a/northd/ovn_northd.dl
+++ b/northd/ovn_northd.dl
@@ -126,7 +126,7 @@  OutProxy_Port_Binding(._uuid              = lsp._uuid,
                       .gateway_chassis    = set_empty(),
                       .ha_chassis_group   = sp.hac_group_uuid,
                       .options            = options,
-                      .datapath           = sw.ls._uuid,
+                      .datapath           = sw._uuid,
                       .parent_port        = lsp.parent_name,
                       .tag                = tag,
                       .mac                = lsp.addresses,
@@ -149,7 +149,7 @@  OutProxy_Port_Binding(._uuid              = lsp._uuid,
     },
     var options = {
         var options = lsp.options;
-        match (sw.ls.other_config.get("vlan-passthru")) {
+        match (sw.other_config.get("vlan-passthru")) {
             Some{"true"} -> options.insert("vlan-passthru", "true"),
             _ -> ()
         };
@@ -164,7 +164,7 @@  OutProxy_Port_Binding(._uuid              = lsp._uuid,
                       .gateway_chassis    = set_empty(),
                       .ha_chassis_group   = None,
                       .options            = options,
-                      .datapath           = sw.ls._uuid,
+                      .datapath           = sw._uuid,
                       .parent_port        = lsp.parent_name,
                       .tag                = None,
                       .mac                = lsp.addresses,
@@ -560,7 +560,7 @@  sb::Out_HA_Chassis(ha_chassis_uuid(ha_chassis.chassis_name, hac_uuid), chassis,
 sb::Out_HA_Chassis_Group(_uuid, name, ha_chassis, set_empty() /* XXX? */, eids) :-
     sp in &SwitchPort(),
     sp.lsp.__type == "external",
-    var ls_uuid = sp.sw.ls._uuid,
+    var ls_uuid = sp.sw._uuid,
     Some{var ha_chassis_group_uuid} = sp.lsp.ha_chassis_group,
     ha_chassis_group in nb::HA_Chassis_Group(._uuid = ha_chassis_group_uuid, .name = name,
                                             .external_ids = eids),
@@ -791,7 +791,7 @@  sb::Out_Port_Group(._uuid = hash128(sb_name), .name = sb_name, .ports = port_nam
     PortGroupPort(.pg_uuid = _uuid, .pg_name = nb_name, .port = port_uuid),
     &SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{._uuid = port_uuid,
                                                    .name = port_name},
-                .sw = &Switch{.ls = nb::Logical_Switch{._uuid = ls_uuid}}),
+                .sw = &Switch{._uuid = ls_uuid}),
     TunKeyAllocation(.datapath = ls_uuid, .tunkey = tunkey),
     var sb_name = "${tunkey}_${nb_name}",
     var port_names = port_name.group_by((_uuid, sb_name)).to_set().
@@ -847,9 +847,9 @@  sb::Out_Multicast_Group (._uuid      = hash128((datapath,name)),
                         .name       = name,
                         .tunnel_key = tunnel_key,
                         .ports      = port_ids) :-
-    &SwitchPort(.lsp = lsp, .sw = &Switch{.ls = ls}),
+    &SwitchPort(.lsp = lsp, .sw = sw),
     lsp.is_enabled(),
-    var datapath = ls._uuid,
+    var datapath = sw._uuid,
     var port_ids = lsp._uuid.group_by((datapath)).to_set(),
     (var name, var tunnel_key) = mC_FLOOD().
 
@@ -860,10 +860,10 @@  sb::Out_Multicast_Group (._uuid      = hash128((datapath,name)),
                         .name       = name,
                         .tunnel_key = tunnel_key,
                         .ports      = port_ids) :-
-    &SwitchPort(.lsp = lsp, .sw = &Switch{.ls = ls}),
+    &SwitchPort(.lsp = lsp, .sw = sw),
     lsp.is_enabled(),
     lsp.__type != "router",
-    var datapath = ls._uuid,
+    var datapath = sw._uuid,
     var port_ids = lsp._uuid.group_by((datapath)).to_set(),
     (var name, var tunnel_key) = mC_FLOOD_L2().
 
@@ -880,8 +880,8 @@  sb::Out_Multicast_Group (._uuid      = hash128((ls,name)),
 /* Create a multicast group to flood multicast traffic to routers with
  * multicast relay enabled.
  */
-sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
-                        .datapath = sw.ls._uuid,
+sb::Out_Multicast_Group (._uuid    = hash128((sw._uuid,name)),
+                        .datapath = sw._uuid,
                         .name = name,
                         .tunnel_key = tunnel_key,
                         .ports = port_ids) :-
@@ -892,8 +892,8 @@  sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
 /* Create a multicast group to flood traffic (no reports) to ports with
  * multicast flood enabled.
  */
-sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
-                        .datapath = sw.ls._uuid,
+sb::Out_Multicast_Group (._uuid    = hash128((sw._uuid,name)),
+                        .datapath = sw._uuid,
                         .name = name,
                         .tunnel_key = tunnel_key,
                         .ports = port_ids) :-
@@ -904,8 +904,8 @@  sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
 /* Create a multicast group to flood reports to ports with
  * multicast flood_reports enabled.
  */
-sb::Out_Multicast_Group (._uuid    = hash128((sw.ls._uuid,name)),
-                        .datapath = sw.ls._uuid,
+sb::Out_Multicast_Group (._uuid    = hash128((sw._uuid,name)),
+                        .datapath = sw._uuid,
                         .name = name,
                         .tunnel_key = tunnel_key,
                         .ports = port_ids) :-
@@ -928,7 +928,7 @@  sb::Out_Multicast_Group (._uuid    = hash128((rtr._uuid,name)),
 /* Create a multicast group for each IGMP group learned by a Switch.
  * 'tunnel_key' == 0 triggers an ID allocation later.
  */
-OutProxy_Multicast_Group (.datapath   = switch.ls._uuid,
+OutProxy_Multicast_Group (.datapath   = switch._uuid,
                           .name       = address,
                           .ports      = port_ids) :-
     IgmpSwitchMulticastGroup(address, &switch, port_ids).
@@ -1694,14 +1694,15 @@  for (f in AggregatedFlow()) {
 }
 
 /* Logical flows for forwarding groups. */
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage            = s_SWITCH_IN_ARP_ND_RSP(),
      .priority         = 50,
      .__match          = __match,
      .actions          = actions,
      .external_ids     = stage_hint(fg_uuid)) :-
     sw in &Switch(),
-    var fg_uuid = FlatMap(sw.ls.forwarding_groups),
+    nb::Logical_Switch(._uuid = sw._uuid, .forwarding_groups = forwarding_groups),
+    var fg_uuid = FlatMap(forwarding_groups),
     fg in nb::Forwarding_Group(._uuid = fg_uuid),
     not fg.child_port.is_empty(),
     var __match = "arp.tpa == ${fg.vip} && arp.op == 1",
@@ -1723,14 +1724,15 @@  function escape_child_ports(child_port: Set<string>): string {
     };
     escaped.join(",")
 }
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage            = s_SWITCH_IN_L2_LKUP(),
      .priority         = 50,
      .__match          = __match,
      .actions          = actions,
      .external_ids     = map_empty()) :-
     sw in &Switch(),
-    var fg_uuid = FlatMap(sw.ls.forwarding_groups),
+    nb::Logical_Switch(._uuid = sw._uuid, .forwarding_groups = forwarding_groups),
+    var fg_uuid = FlatMap(forwarding_groups),
     fg in nb::Forwarding_Group(._uuid = fg_uuid),
     not fg.child_port.is_empty(),
     var __match = "eth.dst == ${fg.vmac}",
@@ -1743,7 +1745,7 @@  Flow(.logical_datapath = sw.ls._uuid,
 for (sw in &Switch()) {
     if (not sw.is_vlan_transparent) {
         /* Block logical VLANs. */
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_PORT_SEC_L2(),
              .priority         = 100,
              .__match          = "vlan.present",
@@ -1752,7 +1754,7 @@  for (sw in &Switch()) {
     };
 
     /* Broadcast/multicast source address is invalid */
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_IN_PORT_SEC_L2(),
          .priority         = 100,
          .__match          = "eth.src[40]",
@@ -1809,29 +1811,29 @@  function build_port_security_ipv6_nd_flow(
 }
 
 /* Pre-ACL */
-for (&Switch(.ls =ls)) {
+for (&Switch(._uuid =ls_uuid)) {
     /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
      * allowed by default. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
 
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 110,
          .__match          = "eth.dst == $svc_monitor_mac",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 110,
          .__match          = "eth.src == $svc_monitor_mac",
@@ -1846,7 +1848,7 @@  for (&Switch(.ls =ls)) {
 
 for (&SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{.__type = "router"},
                  .json_name = lsp_name,
-                 .sw = &Switch{.ls = ls, .has_stateful_acl = true})) {
+                 .sw = &Switch{._uuid = ls_uuid, .has_stateful_acl = true})) {
     /* Can't use ct() for router ports. Consider the
      * following configuration: lp1(10.0.0.2) on
      * hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
@@ -1859,13 +1861,13 @@  for (&SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{.__type = "router"},
      * as the icmp request went through the logical router
      * on hostA, not hostB. This would only work with
      * distributed conntrack state across all chassis. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 110,
          .__match          = "ip && inport == ${lsp_name}",
          .actions          = "next;",
          .external_ids     = stage_hint(lsp._uuid));
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 110,
          .__match          = "ip && outport == ${lsp_name}",
@@ -1875,14 +1877,14 @@  for (&SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{.__type = "router"},
 
 for (&SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{.__type = "localnet"},
                  .json_name = lsp_name,
-                 .sw = &Switch{.ls = ls, .has_stateful_acl = true})) {
-    Flow(.logical_datapath = ls._uuid,
+                 .sw = &Switch{._uuid = ls_uuid, .has_stateful_acl = true})) {
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 110,
          .__match          = "ip && inport == ${lsp_name}",
          .actions          = "next;",
          .external_ids     = stage_hint(lsp._uuid));
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 110,
          .__match          = "ip && outport == ${lsp_name}",
@@ -1890,19 +1892,19 @@  for (&SwitchPort(.lsp = lsp@nb::Logical_Switch_Port{.__type = "localnet"},
          .external_ids     = stage_hint(lsp._uuid))
 }
 
-for (&Switch(.ls = ls, .has_stateful_acl = true)) {
+for (&Switch(._uuid = ls_uuid, .has_stateful_acl = true)) {
     /* Ingress and Egress Pre-ACL Table (Priority 110).
      *
      * Not to do conntrack on ND and ICMP destination
      * unreachable packets. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 110,
          .__match          = "nd || nd_rs || nd_ra || mldv1 || mldv2 || "
                              "(udp && udp.src == 546 && udp.dst == 547)",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 110,
          .__match          = "nd || nd_rs || nd_ra || mldv1 || mldv2 || "
@@ -1918,13 +1920,13 @@  for (&Switch(.ls = ls, .has_stateful_acl = true)) {
      *
      * 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
      * it to conntrack for tracking and defragmentation. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_ACL(),
          .priority         = 100,
          .__match          = "ip",
          .actions          = "${rEGBIT_CONNTRACK_DEFRAG()} = 1; next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_ACL(),
          .priority         = 100,
          .__match          = "ip",
@@ -1933,16 +1935,16 @@  for (&Switch(.ls = ls, .has_stateful_acl = true)) {
 }
 
 /* Pre-LB */
-for (&Switch(.ls = ls)) {
+for (&Switch(._uuid = ls_uuid)) {
     /* Do not send ND packets to conntrack */
     var __match = "nd || nd_rs || nd_ra || mldv1 || mldv2" in {
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_IN_PRE_LB(),
              .priority         = 110,
              .__match          = __match,
              .actions          = "next;",
              .external_ids     = map_empty());
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_OUT_PRE_LB(),
              .priority         = 110,
              .__match          = __match,
@@ -1951,13 +1953,13 @@  for (&Switch(.ls = ls)) {
     };
 
     /* Do not send service monitor packets to conntrack. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_LB(),
          .priority         = 110,
          .__match          = "eth.dst == $svc_monitor_mac",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_LB(),
          .priority         = 110,
          .__match          = "eth.src == $svc_monitor_mac",
@@ -1965,13 +1967,13 @@  for (&Switch(.ls = ls)) {
          .external_ids     = map_empty());
 
     /* Allow all packets to go to next tables by default. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_LB(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_LB(),
          .priority         = 0,
          .__match          = "1",
@@ -1979,15 +1981,15 @@  for (&Switch(.ls = ls)) {
          .external_ids     = map_empty())
 }
 
-for (&SwitchPort(.lsp = lsp, .json_name = lsp_name, .sw = &Switch{.ls = ls}))
+for (&SwitchPort(.lsp = lsp, .json_name = lsp_name, .sw = &Switch{._uuid = ls_uuid}))
 if (lsp.__type == "router" or lsp.__type == "localnet") {
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_LB(),
          .priority         = 110,
          .__match          = "ip && inport == ${lsp_name}",
          .actions          = "next;",
          .external_ids     = stage_hint(lsp._uuid));
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_LB(),
          .priority         = 110,
          .__match          = "ip && outport == ${lsp_name}",
@@ -2058,7 +2060,7 @@  LoadBalancerEmptyEvents(lb) :-
     var local_events = local_options.get_bool_def("event", false),
     global_events or local_events.
 
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage            = s_SWITCH_IN_PRE_LB(),
      .priority         = 130,
      .__match          = __match,
@@ -2067,7 +2069,7 @@  Flow(.logical_datapath = sw.ls._uuid,
     SwitchLBVIP(.sw_uuid = sw_uuid, .lb = lb, .vip = vip, .backends = backends),
     LoadBalancerEmptyEvents(lb),
     not lb.options.get_bool_def("reject", false),
-    sw in &Switch(.ls = nb::Logical_Switch{._uuid = sw_uuid}),
+    sw in &Switch(._uuid = sw_uuid),
     backends == "",
     HasEventElbMeter(has_elb_meter),
     Some {(var __match, var __action)} = build_empty_lb_event_flow(
@@ -2103,13 +2105,13 @@  Flow(.logical_datapath = sw.ls._uuid,
  * add a lflow to drop ct.inv packets.
  */
 for (sw in &Switch(.has_lb_vip = true)) {
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_IN_PRE_LB(),
          .priority         = 100,
          .__match          = "ip",
          .actions          = "${rEGBIT_CONNTRACK_NAT()} = 1; next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_OUT_PRE_LB(),
          .priority         = 100,
          .__match          = "ip",
@@ -2122,16 +2124,16 @@  relation LbProtocol[string]
 LbProtocol["tcp"].
 LbProtocol["udp"].
 LbProtocol["sctp"].
-for (&Switch(.ls = ls)) {
+for (&Switch(._uuid = ls_uuid)) {
     /* Ingress and Egress pre-stateful Table (Priority 0): Packets are
      * allowed by default. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_STATEFUL(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_STATEFUL(),
          .priority         = 0,
          .__match          = "1",
@@ -2149,14 +2151,14 @@  for (&Switch(.ls = ls)) {
      * transport port to be used when detecting hairpin packets.
      */
     for (LbProtocol[protocol]) {
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_IN_PRE_STATEFUL(),
              .priority         = 120,
              .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip4 && ${protocol}",
              .actions          = "${rEG_ORIG_DIP_IPV4()} = ip4.dst; "
                                  "${rEG_ORIG_TP_DPORT()} = ${protocol}.dst; ct_lb;",
              .external_ids     = map_empty());
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_IN_PRE_STATEFUL(),
              .priority         = 120,
              .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1 && ip6 && ${protocol}",
@@ -2165,14 +2167,14 @@  for (&Switch(.ls = ls)) {
              .external_ids     = map_empty())
     };
 
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_STATEFUL(),
          .priority         = 110,
          .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1",
          .actions          = "ct_lb;",
          .external_ids     = map_empty());
 
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_STATEFUL(),
          .priority         = 110,
          .__match          = "${rEGBIT_CONNTRACK_NAT()} == 1",
@@ -2181,13 +2183,13 @@  for (&Switch(.ls = ls)) {
 
     /* If rEGBIT_CONNTRACK_DEFRAG() is set as 1, then the packets should be
      * sent to conntrack for tracking and defragmentation. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PRE_STATEFUL(),
          .priority         = 100,
          .__match          = "${rEGBIT_CONNTRACK_DEFRAG()} == 1",
          .actions          = "ct_next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PRE_STATEFUL(),
          .priority         = 100,
          .__match          = "${rEGBIT_CONNTRACK_DEFRAG()} == 1",
@@ -2303,19 +2305,19 @@  for (UseCtInvMatch[use_ct_inv_match]) {
         true -> ("ct.inv || ", "&& !ct.inv "),
         false -> ("", ""),
     } in
-    for (sw in &Switch(.ls = ls))
+    for (sw in &Switch(._uuid = ls_uuid))
     var has_stateful = sw.has_stateful_acl or sw.has_lb_vip in
     {
         /* Ingress and Egress ACL Table (Priority 0): Packets are allowed by
          * default.  A related rule at priority 1 is added below if there
          * are any stateful ACLs in this datapath. */
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_IN_ACL(),
              .priority         = 0,
              .__match          = "1",
              .actions          = "next;",
              .external_ids     = map_empty());
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_OUT_ACL(),
              .priority         = 0,
              .__match          = "1",
@@ -2344,13 +2346,13 @@  for (UseCtInvMatch[use_ct_inv_match]) {
              * which will be done by ct_commit() in the "stateful" stage.
              * Subsequent packets will hit the flow at priority 0 that just
              * uses "next;". */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_IN_ACL(),
                  .priority         = 1,
                  .__match          = "ip && (!ct.est || (ct.est && ct_label.blocked == 1))",
                  .actions          = "${rEGBIT_CONNTRACK_COMMIT()} = 1; next;",
                  .external_ids     = map_empty());
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 1,
                  .__match          = "ip && (!ct.est || (ct.est && ct_label.blocked == 1))",
@@ -2364,13 +2366,13 @@  for (UseCtInvMatch[use_ct_inv_match]) {
              * for deletion (bit 0 of ct_label is set).
              *
              * This is enforced at a higher priority than ACLs can be defined. */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_IN_ACL(),
                  .priority         = 65535,
                  .__match          = ct_inv_or ++ "(ct.est && ct.rpl && ct_label.blocked == 1)",
                  .actions          = "drop;",
                  .external_ids     = map_empty());
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 65535,
                  .__match          = ct_inv_or ++ "(ct.est && ct.rpl && ct_label.blocked == 1)",
@@ -2386,14 +2388,14 @@  for (UseCtInvMatch[use_ct_inv_match]) {
              * direction to hit the currently defined policy from ACLs.
              *
              * This is enforced at a higher priority than ACLs can be defined. */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_IN_ACL(),
                  .priority         = 65535,
                  .__match          = "ct.est && !ct.rel && !ct.new " ++ and_not_ct_inv ++
                                      "&& ct.rpl && ct_label.blocked == 0",
                  .actions          = "next;",
                  .external_ids     = map_empty());
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 65535,
                  .__match          = "ct.est && !ct.rel && !ct.new " ++ and_not_ct_inv ++
@@ -2412,14 +2414,14 @@  for (UseCtInvMatch[use_ct_inv_match]) {
              * a dynamically negotiated FTP data channel), but will allow
              * related traffic such as an ICMP Port Unreachable through
              * that's generated from a non-listening UDP port.  */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_IN_ACL(),
                  .priority         = 65535,
                  .__match          = "!ct.est && ct.rel && !ct.new " ++ and_not_ct_inv ++
                                      "&& ct_label.blocked == 0",
                  .actions          = "next;",
                  .external_ids     = map_empty());
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 65535,
                  .__match          = "!ct.est && ct.rel && !ct.new " ++ and_not_ct_inv ++
@@ -2430,13 +2432,13 @@  for (UseCtInvMatch[use_ct_inv_match]) {
             /* Ingress and Egress ACL Table (Priority 65535).
              *
              * Not to do conntrack on ND packets. */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_IN_ACL(),
                  .priority         = 65535,
                  .__match          = "nd || nd_ra || nd_rs || mldv1 || mldv2",
                  .actions          = "next;",
                  .external_ids     = map_empty());
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 65535,
                  .__match          = "nd || nd_ra || nd_rs || mldv1 || mldv2",
@@ -2448,7 +2450,7 @@  for (UseCtInvMatch[use_ct_inv_match]) {
          * if the CMS has configured DNS records for the datapath.
          */
         if (sw.has_dns_records) {
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = ls_uuid,
                  .stage            = s_SWITCH_OUT_ACL(),
                  .priority         = 34000,
                  .__match          = "udp.src == 53",
@@ -2458,13 +2460,13 @@  for (UseCtInvMatch[use_ct_inv_match]) {
 
         /* Add a 34000 priority flow to advance the service monitor reply
          * packets to skip applying ingress ACLs. */
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_IN_ACL(),
              .priority         = 34000,
              .__match          = "eth.dst == $svc_monitor_mac",
              .actions          = "next;",
              .external_ids     = map_empty());
-        Flow(.logical_datapath = ls._uuid,
+        Flow(.logical_datapath = ls_uuid,
              .stage            = s_SWITCH_OUT_ACL(),
              .priority         = 34000,
              .__match          = "eth.src == $svc_monitor_mac",
@@ -2488,10 +2490,10 @@  for (UseCtInvMatch[use_ct_inv_match]) {
 input relation AclHintStages[Stage]
 AclHintStages[s_SWITCH_IN_ACL_HINT()].
 AclHintStages[s_SWITCH_OUT_ACL_HINT()].
-for (sw in &Switch(.ls = ls)) {
+for (sw in &Switch(._uuid = ls_uuid)) {
     for (AclHintStages[stage]) {
         /* In any case, advance to the next stage. */
-        Flow(ls._uuid, stage, 0, "1", "next;", map_empty())
+        Flow(ls_uuid, stage, 0, "1", "next;", map_empty())
     };
 
     for (AclHintStages[stage])
@@ -2500,7 +2502,7 @@  for (sw in &Switch(.ls = ls)) {
          * or drop ACLs. For allow ACLs, the connection must also be committed
          * to conntrack so we set REGBIT_ACL_HINT_ALLOW_NEW.
          */
-        Flow(ls._uuid, stage, 7, "ct.new && !ct.est",
+        Flow(ls_uuid, stage, 7, "ct.new && !ct.est",
              "${rEGBIT_ACL_HINT_ALLOW_NEW()} = 1; "
              "${rEGBIT_ACL_HINT_DROP()} = 1; "
              "next;", map_empty());
@@ -2513,13 +2515,13 @@  for (sw in &Switch(.ls = ls)) {
          *   REGBIT_ACL_HINT_ALLOW_NEW.
          * - drop ACLs.
          */
-        Flow(ls._uuid, stage, 6, "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1",
+        Flow(ls_uuid, stage, 6, "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 1",
              "${rEGBIT_ACL_HINT_ALLOW_NEW()} = 1; "
              "${rEGBIT_ACL_HINT_DROP()} = 1; "
              "next;", map_empty());
 
         /* Not tracked traffic can either be allowed or dropped. */
-        Flow(ls._uuid, stage, 5, "!ct.trk",
+        Flow(ls_uuid, stage, 5, "!ct.trk",
              "${rEGBIT_ACL_HINT_ALLOW()} = 1; "
              "${rEGBIT_ACL_HINT_DROP()} = 1; "
              "next;", map_empty());
@@ -2532,7 +2534,7 @@  for (sw in &Switch(.ls = ls)) {
          *   connection must be committed with ct_label.blocked set so we set
          *   REGBIT_ACL_HINT_BLOCK.
          */
-        Flow(ls._uuid, stage, 4, "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0",
+        Flow(ls_uuid, stage, 4, "!ct.new && ct.est && !ct.rpl && ct_label.blocked == 0",
              "${rEGBIT_ACL_HINT_ALLOW()} = 1; "
              "${rEGBIT_ACL_HINT_BLOCK()} = 1; "
              "next;", map_empty());
@@ -2540,10 +2542,10 @@  for (sw in &Switch(.ls = ls)) {
         /* Not established or established and already blocked connections may
          * hit drop ACLs.
          */
-        Flow(ls._uuid, stage, 3, "!ct.est",
+        Flow(ls_uuid, stage, 3, "!ct.est",
              "${rEGBIT_ACL_HINT_DROP()} = 1; "
              "next;", map_empty());
-        Flow(ls._uuid, stage, 2, "ct.est && ct_label.blocked == 1",
+        Flow(ls_uuid, stage, 2, "ct.est && ct_label.blocked == 1",
              "${rEGBIT_ACL_HINT_DROP()} = 1; "
              "next;", map_empty());
 
@@ -2551,14 +2553,14 @@  for (sw in &Switch(.ls = ls)) {
          * drop ACLs in which case the connection must be committed with
          * ct_label.blocked set.
          */
-        Flow(ls._uuid, stage, 1, "ct.est && ct_label.blocked == 0",
+        Flow(ls_uuid, stage, 1, "ct.est && ct_label.blocked == 0",
              "${rEGBIT_ACL_HINT_BLOCK()} = 1; "
              "next;", map_empty())
     }
 }
 
 /* Ingress or Egress ACL Table (Various priorities). */
-for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_meter)) {
+for (&SwitchACL(.sw = sw, .acl = &acl, .has_fair_meter = fair_meter)) {
     /* consider_acl */
     var has_stateful = sw.has_stateful_acl or sw.has_lb_vip in
     var ingress = acl.direction == "from-lport" in
@@ -2573,7 +2575,7 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
          * may and then its return traffic would not have an
          * associated conntrack entry and would return "+invalid". */
         if (not has_stateful) {
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = stage,
                  .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                  .__match          = acl.__match,
@@ -2592,7 +2594,7 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
              * by ct_commit in the "stateful" stage) to indicate that the
              * connection should be allowed to resume.
              */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = stage,
                  .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                  .__match          = "${rEGBIT_ACL_HINT_ALLOW_NEW()} == 1 && (${acl.__match})",
@@ -2605,7 +2607,7 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
              * proceed to the next table. We use this to ensure that this
              * connection is still allowed by the currently defined
              * policy. Match untracked packets too. */
-            Flow(.logical_datapath = ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = stage,
                  .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                  .__match          = "${rEGBIT_ACL_HINT_ALLOW()} == 1 && (${acl.__match})",
@@ -2622,9 +2624,9 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
              * connection, then we can simply reject/drop it. */
             var __match = "${rEGBIT_ACL_HINT_DROP()} == 1" in
             if (acl.action == "reject") {
-                Reject(ls._uuid, pipeline, stage, acl, fair_meter, __match, "")
+                Reject(sw._uuid, pipeline, stage, acl, fair_meter, __match, "")
             } else {
-                Flow(.logical_datapath = ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = stage,
                      .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                      .__match          = __match ++ " && (${acl.__match})",
@@ -2645,9 +2647,9 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
             var __match = "${rEGBIT_ACL_HINT_BLOCK()} == 1" in
             var actions = "ct_commit { ct_label.blocked = 1; }; " in
             if (acl.action == "reject") {
-                Reject(ls._uuid, pipeline, stage, acl, fair_meter, __match, actions)
+                Reject(sw._uuid, pipeline, stage, acl, fair_meter, __match, actions)
             } else {
-                Flow(.logical_datapath = ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = stage,
                      .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                      .__match          = __match ++ " && (${acl.__match})",
@@ -2659,9 +2661,9 @@  for (&SwitchACL(.sw = sw@&Switch{.ls = ls}, .acl = &acl, .has_fair_meter = fair_
              * so a "reject/drop" ACL is simply the "reject/drop"
              * logical flow action in all cases. */
             if (acl.action == "reject") {
-                Reject(ls._uuid, pipeline, stage, acl, fair_meter, "", "")
+                Reject(sw._uuid, pipeline, stage, acl, fair_meter, "", "")
             } else {
-                Flow(.logical_datapath = ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = stage,
                      .priority         = acl.priority + oVN_ACL_PRI_OFFSET(),
                      .__match          = acl.__match,
@@ -2681,7 +2683,7 @@  for (SwitchPortDHCPv4Options(.port = &SwitchPort{.lsp = lsp, .sw = &sw},
     (Some{var server_id}, Some{var server_mac}, Some{var lease_time}) =
         (options.get("server_id"), options.get("server_mac"), options.get("lease_time")) in
     var has_stateful = sw.has_stateful_acl or sw.has_lb_vip in
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_OUT_ACL(),
          .priority         = 34000,
          .__match          = "outport == ${json_string_escape(lsp.name)} "
@@ -2701,7 +2703,7 @@  for (SwitchPortDHCPv6Options(.port = &SwitchPort{.lsp = lsp, .sw = &sw},
     /* Get the link local IP of the DHCPv6 server from the
      * server MAC. */
     var has_stateful = sw.has_stateful_acl or sw.has_lb_vip in
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_OUT_ACL(),
          .priority         = 34000,
          .__match          = "outport == ${json_string_escape(lsp.name)} "
@@ -2720,26 +2722,26 @@  QoSAction(qos, k, v) :-
     (var k, var v) = action.
 
 /* QoS rules */
-for (&Switch(.ls = ls)) {
-    Flow(.logical_datapath = ls._uuid,
+for (&Switch(._uuid = ls_uuid)) {
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_QOS_MARK(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_QOS_MARK(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_QOS_METER(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_QOS_METER(),
          .priority         = 0,
          .__match          = "1",
@@ -2754,7 +2756,7 @@  for (SwitchQoS(.sw = &sw, .qos = &qos)) {
         /* FIXME: Can value_action be negative? */
         for (QoSAction(qos._uuid, key_action, value_action)) {
             if (key_action == "dscp") {
-                Flow(.logical_datapath = sw.ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = stage,
                      .priority         = qos.priority,
                      .__match          = qos.__match,
@@ -2788,7 +2790,7 @@  for (SwitchQoS(.sw = &sw, .qos = &qos)) {
              *
              * We limit the bandwidth of this flow by adding a meter table.
              */
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = stage,
                  .priority         = qos.priority,
                  .__match          = qos.__match,
@@ -2799,16 +2801,16 @@  for (SwitchQoS(.sw = &sw, .qos = &qos)) {
 }
 
 /* stateful rules */
-for (&Switch(.ls = ls)) {
+for (&Switch(._uuid = ls_uuid)) {
     /* Ingress and Egress stateful Table (Priority 0): Packets are
      * allowed by default. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_STATEFUL(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_STATEFUL(),
          .priority         = 0,
          .__match          = "1",
@@ -2819,13 +2821,13 @@  for (&Switch(.ls = ls)) {
      * committed to conntrack. We always set ct_label.blocked to 0 here as
      * any packet that makes it this far is part of a connection we
      * want to allow to continue. */
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_STATEFUL(),
          .priority         = 100,
          .__match          = "${rEGBIT_CONNTRACK_COMMIT()} == 1",
          .actions          = "ct_commit { ct_label.blocked = 0; }; next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_STATEFUL(),
          .priority         = 100,
          .__match          = "${rEGBIT_CONNTRACK_COMMIT()} == 1",
@@ -2906,7 +2908,7 @@  function build_lb_vip_actions(lbvip: Ref<LBVIPWithStatus>,
                         lbvip.lb.protocol);
     actions0 ++ actions
 }
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage            = s_SWITCH_IN_STATEFUL(),
      .priority         = priority,
      .__match          = __match,
@@ -2914,7 +2916,7 @@  Flow(.logical_datapath = sw.ls._uuid,
      .external_ids     = stage_hint(lb._uuid)) :-
     sw in &Switch(),
     LBVIPWithStatus[lbvip@&LBVIPWithStatus{.lb = lb}],
-    sw.ls.load_balancer.contains(lb._uuid),
+    sw.load_balancer.contains(lb._uuid),
     var priority = if (lbvip.vip_port != 0) { 120 } else { 110 },
     var actions = {
         /* Store the original destination IP to be used when generating
@@ -2947,12 +2949,12 @@  Flow(.logical_datapath = ls_uuid,
      .__match = "1",
      .actions = "next;",
      .external_ids = map_empty()) :-
-     &Switch(.ls = nb::Logical_Switch{._uuid = ls_uuid}),
+     &Switch(._uuid = ls_uuid),
      var stages = [s_SWITCH_IN_PRE_HAIRPIN(),
                    s_SWITCH_IN_NAT_HAIRPIN(),
                    s_SWITCH_IN_HAIRPIN()],
      var stage = FlatMap(stages).
-for (&Switch(.ls = nb::Logical_Switch{._uuid = ls_uuid}, .has_lb_vip = true)) {
+for (&Switch(._uuid = ls_uuid, .has_lb_vip = true)) {
     /* Check if the packet needs to be hairpinned.
      * Set REGBIT_HAIRPIN in the original direction and
      * REGBIT_HAIRPIN_REPLY in the reply direction.
@@ -3020,7 +3022,7 @@  for (&SwitchPort(.lsp = lsp, .sw = &sw, .json_name = json_name, .ps_eth_addresse
                 None -> "next;",
                 Some{id} -> "set_queue(${id}); next;"
             } in
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_PORT_SEC_L2(),
              .priority         = 50,
              .__match          = __match,
@@ -3055,7 +3057,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
                          " && ip4.src == 0.0.0.0"
                          " && ip4.dst == 255.255.255.255"
                          " && udp.src == 68 && udp.dst == 67" in {
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = s_SWITCH_IN_PORT_SEC_IP(),
                  .priority         = 90,
                  .__match          = dhcp_match,
@@ -3078,7 +3080,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
             "inport == ${port.json_name} && eth.src == ${ps.ea} && ip4.src == {" ++
             addrs.join(", ") ++ "}" in
         {
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage         = s_SWITCH_IN_PORT_SEC_IP(),
                  .priority         = 90,
                  .__match          = __match,
@@ -3093,7 +3095,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
                         " && ip6.dst == ff02::/16"
                         " && icmp6.type == {131, 135, 143}" in
         {
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = s_SWITCH_IN_PORT_SEC_IP(),
                  .priority         = 90,
                  .__match          = dad_match,
@@ -3103,7 +3105,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
         var __match = "inport == ${port.json_name} && eth.src == ${ps.ea}" ++
                       build_port_security_ipv6_flow(Ingress, ps.ea, ps.ipv6_addrs) in
         {
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = s_SWITCH_IN_PORT_SEC_IP(),
                  .priority         = 90,
                  .__match          = __match,
@@ -3113,7 +3115,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
     };
     var __match = "inport == ${port.json_name} && eth.src == ${ps.ea} && ip" in
     {
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_PORT_SEC_IP(),
              .priority         = 80,
              .__match          = __match,
@@ -3161,7 +3163,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
                     prefix
                 }
             } in {
-                Flow(.logical_datapath = sw.ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = s_SWITCH_IN_PORT_SEC_ND(),
                      .priority         = 90,
                      .__match          = __match,
@@ -3173,7 +3175,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
             var __match = "inport == ${port.json_name} && eth.src == ${ps.ea}" ++
                           build_port_security_ipv6_nd_flow(ps.ea, ps.ipv6_addrs) in
             {
-                Flow(.logical_datapath = sw.ls._uuid,
+                Flow(.logical_datapath = sw._uuid,
                      .stage            = s_SWITCH_IN_PORT_SEC_ND(),
                      .priority         = 90,
                      .__match          = __match,
@@ -3181,7 +3183,7 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
                      .external_ids     = stage_hint(port.lsp._uuid))
             }
         };
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_PORT_SEC_ND(),
              .priority         = 80,
              .__match          = "inport == ${port.json_name} && (arp || nd)",
@@ -3192,14 +3194,14 @@  for (SwitchPortPSAddresses(.port = &port@SwitchPort{.sw = &sw}, .ps_addrs = ps)
 
 /* Ingress table PORT_SEC_ND and PORT_SEC_IP: Port security - IP and ND, by
  * default goto next.  (priority 0)*/
-for (&Switch(.ls = ls)) {
-    Flow(.logical_datapath = ls._uuid,
+for (&Switch(._uuid = ls_uuid)) {
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PORT_SEC_ND(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_PORT_SEC_IP(),
          .priority         = 0,
          .__match          = "1",
@@ -3214,7 +3216,7 @@  for (&SwitchPort(.lsp = lsp, .sw = &sw, .json_name = json_name)
      if lsp.is_enabled() and
         (lsp.__type == "localnet" or lsp.__type == "vtep"))
 {
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_IN_ARP_ND_RSP(),
          .priority         = 100,
          .__match          = "inport == ${json_name}",
@@ -3235,7 +3237,7 @@  function lsp_is_up(lsp: nb::Logical_Switch_Port): bool = {
  *  - ARP reply from the virtual ip which belongs to a logical
  *    port of type 'virtual' and bind that port.
  * */
- Flow(.logical_datapath = sp.sw.ls._uuid,
+ Flow(.logical_datapath = sp.sw._uuid,
       .stage            = s_SWITCH_IN_ARP_ND_RSP(),
       .priority         = 100,
       .__match          = "inport == ${vp.json_name} && "
@@ -3278,7 +3280,7 @@  for (CheckLspIsUp[check_lsp_is_up]) {
                           "outport = inport; "
                           "flags.loopback = 1; "
                           "output;" in
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = s_SWITCH_IN_ARP_ND_RSP(),
                  .priority         = 50,
                  .__match          = __match,
@@ -3297,7 +3299,7 @@  for (CheckLspIsUp[check_lsp_is_up]) {
              * detect situations where the network is not working as
              * configured, so dropping the request would frustrate that
              * intent.) */
-            Flow(.logical_datapath = sw.ls._uuid,
+            Flow(.logical_datapath = sw._uuid,
                  .stage            = s_SWITCH_IN_ARP_ND_RSP(),
                  .priority         = 100,
                  .__match          = __match ++ " && inport == ${json_name}",
@@ -3327,7 +3329,7 @@  for (SwitchPortIPv6Address(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
                   "output; "
                   "};" in
     {
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_ARP_ND_RSP(),
              .priority         = 50,
              .__match          = __match,
@@ -3336,7 +3338,7 @@  for (SwitchPortIPv6Address(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
 
         /* Do not reply to a solicitation from the port that owns the
          * address (otherwise DAD detection will fail). */
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_ARP_ND_RSP(),
              .priority         = 100,
              .__match          = __match ++ " && inport == ${json_name}",
@@ -3358,7 +3360,7 @@  for (ls in nb::Logical_Switch) {
 
 /* Ingress table ARP_ND_RSP: ARP/ND responder for service monitor source ip.
  * (priority 110)*/
-Flow(.logical_datapath = sp.sw.ls._uuid,
+Flow(.logical_datapath = sp.sw._uuid,
      .stage            = s_SWITCH_IN_ARP_ND_RSP(),
      .priority         = 110,
      .__match          = "arp.tpa == ${svc_mon_src_ip} && arp.op == 1",
@@ -3740,7 +3742,7 @@  for (ls in nb::Logical_Switch) {
          .external_ids     = map_empty())
 }
 
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage = s_SWITCH_IN_L2_LKUP(),
      .priority = 110,
      .__match = "eth.dst == $svc_monitor_mac",
@@ -3748,7 +3750,7 @@  Flow(.logical_datapath = sw.ls._uuid,
      .external_ids = map_empty()) :-
     sw in &Switch().
 
-for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
+for (sw in &Switch(._uuid = ls_uuid, .mcast_cfg = &mcast_cfg)
         if (mcast_cfg.enabled)) {
     for (SwitchMcastFloodRelayPorts(sw, relay_ports)) {
         for (SwitchMcastFloodReportPorts(sw, flood_report_ports)) {
@@ -3768,7 +3770,7 @@  for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
                     }
                 } in {
                     /* Punt IGMP traffic to controller. */
-                    UniqueFlow[Flow{.logical_datapath = ls._uuid,
+                    UniqueFlow[Flow{.logical_datapath = ls_uuid,
                                     .stage            = s_SWITCH_IN_L2_LKUP(),
                                     .priority         = 100,
                                     .__match          = "ip4 && ip.proto == 2",
@@ -3776,7 +3778,7 @@  for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
                                     .external_ids     = map_empty()}];
 
                     /* Punt MLD traffic to controller. */
-                    UniqueFlow[Flow{.logical_datapath = ls._uuid,
+                    UniqueFlow[Flow{.logical_datapath = ls_uuid,
                                     .stage            = s_SWITCH_IN_L2_LKUP(),
                                     .priority         = 100,
                                     .__match          = "mldv1 || mldv2",
@@ -3787,7 +3789,7 @@  for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
                      * all ports - RFC 4541, section 2.1.2, item 2.
                      */
                     var flood = json_string_escape(mC_FLOOD().0) in
-                    UniqueFlow[Flow{.logical_datapath = ls._uuid,
+                    UniqueFlow[Flow{.logical_datapath = ls_uuid,
                                     .stage            = s_SWITCH_IN_L2_LKUP(),
                                     .priority         = 85,
                                     .__match          = "ip4.mcast && ip4.dst == 224.0.0.0/24",
@@ -3798,7 +3800,7 @@  for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
                      * multicast IPs (RFC 4291, 2.7.1).
                      */
                     var flood = json_string_escape(mC_FLOOD().0) in
-                    UniqueFlow[Flow{.logical_datapath = ls._uuid,
+                    UniqueFlow[Flow{.logical_datapath = ls_uuid,
                                     .stage            = s_SWITCH_IN_L2_LKUP(),
                                     .priority         = 85,
                                     .__match          = "ip6.mcast_flood",
@@ -3837,7 +3839,7 @@  for (sw in &Switch(.ls = ls, .mcast_cfg = &mcast_cfg)
                                 ""
                             }
                         } in
-                        UniqueFlow[Flow{.logical_datapath = ls._uuid,
+                        UniqueFlow[Flow{.logical_datapath = ls_uuid,
                                         .stage            = s_SWITCH_IN_L2_LKUP(),
                                         .priority         = 80,
                                         .__match          = "ip4.mcast || ip6.mcast",
@@ -3891,7 +3893,7 @@  for (IgmpSwitchMulticastGroup(.address = address, .switch = &sw)) {
                     ""
                 }
             } in
-            UniqueFlow[Flow{.logical_datapath = sw.ls._uuid,
+            UniqueFlow[Flow{.logical_datapath = sw._uuid,
                             .stage            = s_SWITCH_IN_L2_LKUP(),
                             .priority         = 90,
                             .__match          = "eth.mcast && ${ipX} && ${ipX}.dst == ${address}",
@@ -3911,7 +3913,7 @@  for (IgmpSwitchMulticastGroup(.address = address, .switch = &sw)) {
  * chassis, drop ARP requests arriving on localnet ports from X's Ethernet
  * address, if the ARP request is asking to translate the IP address of a
  * router port on LS. */
-Flow(.logical_datapath = sp.sw.ls._uuid,
+Flow(.logical_datapath = sp.sw._uuid,
      .stage            = s_SWITCH_IN_EXTERNAL_PORT(),
      .priority         = 100,
      .__match          = ("inport == ${json_string_escape(localnet_port.1)} && "
@@ -3927,7 +3929,7 @@  Flow(.logical_datapath = sp.sw.ls._uuid,
     rp in &SwitchPort(.sw = sp.sw),
     rp.lsp.__type == "router",
     SwitchPortIPv4Address(.port = rp, .addr = rp_addr).
-Flow(.logical_datapath = sp.sw.ls._uuid,
+Flow(.logical_datapath = sp.sw._uuid,
      .stage            = s_SWITCH_IN_EXTERNAL_PORT(),
      .priority         = 100,
      .__match          = ("inport == ${json_string_escape(localnet_port.1)} && "
@@ -3944,7 +3946,7 @@  Flow(.logical_datapath = sp.sw.ls._uuid,
     rp in &SwitchPort(.sw = sp.sw),
     rp.lsp.__type == "router",
     SwitchPortIPv6Address(.port = rp, .addr = rp_addr).
-Flow(.logical_datapath = sp.sw.ls._uuid,
+Flow(.logical_datapath = sp.sw._uuid,
      .stage            = s_SWITCH_IN_EXTERNAL_PORT(),
      .priority         = 100,
      .__match          = ("inport == ${json_string_escape(localnet_port.1)} && "
@@ -3978,7 +3980,7 @@  for (ls in nb::Logical_Switch) {
 for (SwitchPortStaticAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json_name, .sw = &sw},
                                .addrs = addrs)
      if lsp.__type != "external") {
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_IN_L2_LKUP(),
          .priority         = 50,
          .__match          = "eth.dst == ${addrs.ea}",
@@ -4019,7 +4021,7 @@  function lrouter_port_ip_reachable(rp: Ref<RouterPort>, addr: v46_ip): bool {
     };
     false
 }
-UniqueFlow[Flow{.logical_datapath = sw.ls._uuid,
+UniqueFlow[Flow{.logical_datapath = sw._uuid,
                 .stage            = s_SWITCH_IN_L2_LKUP(),
                 .priority         = 75,
                 .__match          = __match,
@@ -4107,7 +4109,7 @@  function get_arp_forward_ips(rp: Ref<RouterPort>): (Set<string>, Set<string>) =
  * delivers to patch ports) but we're bypassing multicast_groups.
  * (This is why we match against fLAGBIT_NOT_VXLAN() here.)
  */
-AnnotatedFlow(.f = Flow{.logical_datapath = sw.ls._uuid,
+AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid,
                         .stage            = s_SWITCH_IN_L2_LKUP(),
                         .priority         = 80,
                         .__match          = fLAGBIT_NOT_VXLAN() ++
@@ -4126,7 +4128,7 @@  AnnotatedFlow(.f = Flow{.logical_datapath = sw.ls._uuid,
     (var all_ips_v4, _) = get_arp_forward_ips(rp),
     not all_ips_v4.is_empty(),
     var mc_flood_l2 = json_string_escape(mC_FLOOD_L2().0).
-AnnotatedFlow(.f = Flow{.logical_datapath = sw.ls._uuid,
+AnnotatedFlow(.f = Flow{.logical_datapath = sw._uuid,
                         .stage            = s_SWITCH_IN_L2_LKUP(),
                         .priority         = 80,
                         .__match          = fLAGBIT_NOT_VXLAN() ++
@@ -4149,7 +4151,7 @@  AnnotatedFlow(.f = Flow{.logical_datapath = sw.ls._uuid,
 for (SwitchPortNewDynamicAddress(.port = &SwitchPort{.lsp = lsp, .json_name = json_name, .sw = &sw},
                                  .address = Some{addrs})
      if lsp.__type != "external") {
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_IN_L2_LKUP(),
          .priority         = 50,
          .__match          = "eth.dst == ${addrs.ea}",
@@ -4190,7 +4192,7 @@  for (&SwitchPort(.lsp = lsp,
         } else {
             "eth.dst == ${mac}"
         } in
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_IN_L2_LKUP(),
              .priority         = 50,
              .__match          = __match,
@@ -4206,7 +4208,7 @@  for (&SwitchPort(.lsp = lsp,
                     Some{var emac} = nat.nat.external_mac in
                     Some{var nat_mac} = eth_addr_from_string(emac) in
                     var __match = "eth.dst == ${nat_mac} && is_chassis_resident(${json_string_escape(lport)})" in
-                    Flow(.logical_datapath = sw.ls._uuid,
+                    Flow(.logical_datapath = sw._uuid,
                          .stage            = s_SWITCH_IN_L2_LKUP(),
                          .priority         = 50,
                          .__match          = __match,
@@ -4227,7 +4229,7 @@  for (&SwitchPort(.lsp = lsp,
         }*/
 
 /* Ingress table L2_LKUP and L2_UNKNOWN: Destination lookup for unknown MACs (priority 0). */
-for (sw in &Switch(.ls = nb::Logical_Switch{._uuid = ls_uuid})) {
+for (sw in &Switch(._uuid = ls_uuid)) {
     Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_IN_L2_LKUP(),
          .priority         = 0,
@@ -4257,14 +4259,14 @@  for (sw in &Switch(.ls = nb::Logical_Switch{._uuid = ls_uuid})) {
 
 /* Egress tables PORT_SEC_IP: Egress port security - IP (priority 0)
  * Egress table PORT_SEC_L2: Egress port security L2 - multicast/broadcast (priority 100). */
-for (&Switch(.ls = ls)) {
-    Flow(.logical_datapath = ls._uuid,
+for (&Switch(._uuid = ls_uuid)) {
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PORT_SEC_IP(),
          .priority         = 0,
          .__match          = "1",
          .actions          = "next;",
          .external_ids     = map_empty());
-    Flow(.logical_datapath = ls._uuid,
+    Flow(.logical_datapath = ls_uuid,
          .stage            = s_SWITCH_OUT_PORT_SEC_L2(),
          .priority         = 100,
          .__match          = "eth.mcast",
@@ -4288,19 +4290,19 @@  Flow(.logical_datapath = ls_uuid,
     sp in &SwitchPort(.lsp = nb::Logical_Switch_Port{._uuid = lsp_uuid, .__type = ""},
                       .ps_addresses = vec_empty()).
 
-Flow(.logical_datapath = ls._uuid,
+Flow(.logical_datapath = ls_uuid,
      .stage            = s_SWITCH_IN_LOOKUP_FDB(),
      .priority         = 0,
      .__match          = "1",
      .actions          = "next;",
      .external_ids     = map_empty()),
-Flow(.logical_datapath = ls._uuid,
+Flow(.logical_datapath = ls_uuid,
      .stage            = s_SWITCH_IN_PUT_FDB(),
      .priority         = 0,
      .__match          = "1",
      .actions          = "next;",
      .external_ids     = map_empty()) :-
-    &Switch(.ls = ls).
+    &Switch(._uuid = ls_uuid).
 
 /* Egress table PORT_SEC_IP: Egress port security - IP (priorities 90 and 80)
  * if port security enabled.
@@ -4311,7 +4313,7 @@  Flow(.logical_datapath = ls._uuid,
  *
  * Priority 150 rules drop packets to disabled logical ports, so that they
  * don't even receive multicast or broadcast packets. */
-Flow(.logical_datapath = sw.ls._uuid,
+Flow(.logical_datapath = sw._uuid,
      .stage            = s_SWITCH_OUT_PORT_SEC_L2(),
      .priority         = 50,
      .__match          = __match,
@@ -4334,7 +4336,7 @@  Flow(.logical_datapath = sw.ls._uuid,
 
 for (&SwitchPort(.lsp = lsp, .json_name = json_name, .sw = &sw)
      if not lsp.is_enabled() and lsp.__type != "external") {
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_OUT_PORT_SEC_L2(),
          .priority         = 150,
          .__match          = "outport == {$json_name}",
@@ -4366,7 +4368,7 @@  for (SwitchPortPSAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
         var __match =
             "outport == ${json_name} && eth.dst == ${ps.ea} && ip4.dst == {255.255.255.255, 224.0.0.0/4, " ++
             addrs.join(", ") ++ "}" in
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_OUT_PORT_SEC_IP(),
              .priority         = 90,
              .__match          = __match,
@@ -4376,7 +4378,7 @@  for (SwitchPortPSAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
     if (ps.ipv6_addrs.len() > 0) {
         var __match = "outport == ${json_name} && eth.dst == ${ps.ea}" ++
                       build_port_security_ipv6_flow(Egress, ps.ea, ps.ipv6_addrs) in
-        Flow(.logical_datapath = sw.ls._uuid,
+        Flow(.logical_datapath = sw._uuid,
              .stage            = s_SWITCH_OUT_PORT_SEC_IP(),
              .priority         = 90,
              .__match          = __match,
@@ -4384,7 +4386,7 @@  for (SwitchPortPSAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json_nam
              .external_ids     = stage_hint(lsp._uuid))
     };
     var __match = "outport == ${json_name} && eth.dst == ${ps.ea} && ip" in
-    Flow(.logical_datapath = sw.ls._uuid,
+    Flow(.logical_datapath = sw._uuid,
          .stage            = s_SWITCH_OUT_PORT_SEC_IP(),
          .priority         = 80,
          .__match          = __match,
@@ -6901,12 +6903,12 @@  Flow(.logical_datapath = lr_uuid,
  * connects) and if the address in question is reachable from the
  * router port, add an ARP/ND entry in that router's pipeline. */
 for (SwitchPortIPv4Address(
-        .port = &SwitchPort{.lsp = lsp, .sw = &sw},
+        .port = &SwitchPort{.lsp = lsp, .sw = sw},
         .ea = ea,
         .addr = addr)
      if lsp.__type != "router" and lsp.__type != "virtual" and lsp.is_enabled())
 {
-    for (&SwitchPort(.sw = &Switch{.ls = nb::Logical_Switch{._uuid = sw.ls._uuid}},
+    for (&SwitchPort(.sw = &Switch{._uuid = sw._uuid},
                      .peer = Some{&peer@RouterPort{.router = &peer_router}}))
     {
         Some{_} = find_lrp_member_ip(peer.networks, IPv4{addr.addr}) in
@@ -6921,12 +6923,12 @@  for (SwitchPortIPv4Address(
 }
 
 for (SwitchPortIPv6Address(
-        .port = &SwitchPort{.lsp = lsp, .sw = &sw},
+        .port = &SwitchPort{.lsp = lsp, .sw = sw},
         .ea = ea,
         .addr = addr)
      if lsp.__type != "router" and lsp.__type != "virtual" and lsp.is_enabled())
 {
-    for (&SwitchPort(.sw = &Switch{.ls = nb::Logical_Switch{._uuid = sw.ls._uuid}},
+    for (&SwitchPort(.sw = &Switch{._uuid = sw._uuid},
                      .peer = Some{&peer@RouterPort{.router = &peer_router}}))
     {
         Some{_} = find_lrp_member_ip(peer.networks, IPv6{addr.addr}) in
@@ -7002,12 +7004,12 @@  Flow(.logical_datapath = peer.router._uuid,
  * the switch in question. */
 for (&SwitchPort(.lsp = lsp1,
                  .peer = Some{&peer1@RouterPort{.router = &peer_router}},
-                 .sw = &sw)
+                 .sw = sw)
      if lsp1.is_enabled() and
         not peer_router.options.get_bool_def("dynamic_neigh_routers", false))
 {
     for (&SwitchPort(.lsp = lsp2, .peer = Some{&peer2},
-                     .sw = &Switch{.ls = nb::Logical_Switch{._uuid = sw.ls._uuid}})
+                     .sw = &Switch{._uuid = sw._uuid})
          /* Skip the router port under consideration. */
          if peer2.lrp._uuid != peer1.lrp._uuid)
     {
@@ -7406,7 +7408,7 @@  function get_port_tunkey(map: Map<string,string>, key: string): Option<integer>
 relation RequestedPortTunKey(datapath: uuid, port: uuid, tunkey: integer)
 RequestedPortTunKey(datapath, port, tunkey) :-
     sp in &SwitchPort(),
-    var datapath = sp.sw.ls._uuid,
+    var datapath = sp.sw._uuid,
     var port = sp.lsp._uuid,
     Some{var tunkey} = get_port_tunkey(sp.lsp.options, "requested-tnl-key").
 RequestedPortTunKey(datapath, port, tunkey) :-