diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java index 6fd7d6de9b6..2405f876325 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java @@ -710,7 +710,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage String bridgeName=generateBridgeNameForVpc(vpcId); OvsVpcPhysicalTopologyConfigCommand topologyConfigCommand = prepareVpcTopologyUpdate(vpcId); - topologyConfigCommand.setSequenceNumber(getNextSequenceNumber(vpcId)); + topologyConfigCommand.setSequenceNumber(getNextTopologyUpdateSequenceNumber(vpcId)); // send topology change update to VPC spanned hosts for (Long id: vpcSpannedHostIds) { @@ -820,7 +820,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage if (network.getVpcId() != null && isVpcEnabledForDistributedRouter(network.getVpcId())) { long vpcId = network.getVpcId(); OvsVpcRoutingPolicyConfigCommand cmd = prepareVpcRoutingPolicyUpdate(vpcId); - cmd.setSequenceNumber(getNextSequenceNumber(vpcId)); + cmd.setSequenceNumber(getNextRoutingPolicyUpdateSequenceNumber(vpcId)); // get the list of hosts on which VPC spans (i.e hosts that need to be aware of VPC // network ACL update) @@ -901,7 +901,7 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage } } - private long getNextSequenceNumber(final long vpcId) { + private long getNextTopologyUpdateSequenceNumber(final long vpcId) { try { return Transaction.execute(new TransactionCallback() { @@ -913,9 +913,31 @@ public class OvsTunnelManagerImpl extends ManagerBase implements OvsTunnelManage _vpcDrSeqNoDao.persist(seqVo); } seqVo = _vpcDrSeqNoDao.lockRow(seqVo.getId(), true); - seqVo.incrSequenceNo(); + seqVo.incrTopologyUpdateSequenceNo(); _vpcDrSeqNoDao.update(seqVo.getId(), seqVo); - return seqVo.getSequenceNo(); + return seqVo.getTopologyUpdateSequenceNo(); + } + }); + } finally { + + } + } + + private long getNextRoutingPolicyUpdateSequenceNumber(final long vpcId) { + + try { + return Transaction.execute(new TransactionCallback() { + @Override + public Long doInTransaction(TransactionStatus status) { + VpcDistributedRouterSeqNoVO seqVo = _vpcDrSeqNoDao.findByVpcId(vpcId); + if (seqVo == null) { + seqVo = new VpcDistributedRouterSeqNoVO(vpcId); + _vpcDrSeqNoDao.persist(seqVo); + } + seqVo = _vpcDrSeqNoDao.lockRow(seqVo.getId(), true); + seqVo.incrPolicyUpdateSequenceNo(); + _vpcDrSeqNoDao.update(seqVo.getId(), seqVo); + return seqVo.getPolicyUpdateSequenceNo(); } }); } finally { diff --git a/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/VpcDistributedRouterSeqNoVO.java b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/VpcDistributedRouterSeqNoVO.java index d87a2c4dc57..613af812d55 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/VpcDistributedRouterSeqNoVO.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/ovs/dao/VpcDistributedRouterSeqNoVO.java @@ -37,8 +37,11 @@ public class VpcDistributedRouterSeqNoVO implements InternalIdentity { @Column(name = "vpc_id", updatable = false, nullable = false) private Long vpcId; - @Column(name = "sequence_no") - long sequenceNo = 0; + @Column(name = "topology_update_sequence_no") + long topologyUpdateSequenceNo = 0; + + @Column(name = "routing_policy__update_sequence_no") + long policyUpdateSequenceNo = 0; protected VpcDistributedRouterSeqNoVO() { @@ -62,11 +65,19 @@ public class VpcDistributedRouterSeqNoVO implements InternalIdentity { this.vpcId = vpcId; } - public long getSequenceNo() { - return sequenceNo; + public long getTopologyUpdateSequenceNo() { + return topologyUpdateSequenceNo; } - public void incrSequenceNo() { - sequenceNo++; + public void incrTopologyUpdateSequenceNo() { + topologyUpdateSequenceNo++; + } + + public long getPolicyUpdateSequenceNo() { + return policyUpdateSequenceNo; + } + + public void incrPolicyUpdateSequenceNo() { + policyUpdateSequenceNo++; } } diff --git a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py index fb28cae4062..b93f87b1700 100644 --- a/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py +++ b/scripts/vm/hypervisor/xenserver/cloudstack_pluginlib.py @@ -344,8 +344,139 @@ def get_acl(vpcconfig, required_acl_id): return acl return None -# Configures the bridge created for a VPC enabled for distributed routing. Management server sends VPC physical topology -# details. Based on the VPC physical topology L2 lookup table and L3 lookup tables are updated by this function. +def check_tunnel_exists(bridge, tunnel_name): + res = do_cmd([VSCTL_PATH, "port-to-br", tunnel_name]) + return res == bridge + +def create_tunnel(bridge, remote_ip, gre_key, src_host, dst_host, network_uuid): + + logging.debug("Creating tunnel from host %s" %src_host + " to host %s" %dst_host + " with GRE key %s" %gre_key) + + res = check_switch() + if res != "SUCCESS": + logging.debug("Openvswitch running: NO") + return "FAILURE:%s" % res + + # We need to keep the name below 14 characters + # src and target are enough - consider a fixed length hash + name = "t%s-%s-%s" % (gre_key, src_host, dst_host) + + # Verify the xapi bridge to be created + # NOTE: Timeout should not be necessary anymore + wait = [VSCTL_PATH, "--timeout=30", "wait-until", "bridge", + bridge, "--", "get", "bridge", bridge, "name"] + res = do_cmd(wait) + if bridge not in res: + logging.debug("WARNING:Can't find bridge %s for creating " + + "tunnel!" % bridge) + return "FAILURE:NO_BRIDGE" + logging.debug("bridge %s for creating tunnel - VERIFIED" % bridge) + tunnel_setup = False + drop_flow_setup = False + try: + # Create a port and configure the tunnel interface for it + add_tunnel = [VSCTL_PATH, "add-port", bridge, + name, "--", "set", "interface", + name, "type=gre", "options:key=%s" % gre_key, + "options:remote_ip=%s" % remote_ip] + do_cmd(add_tunnel) + tunnel_setup = True + # verify port + verify_port = [VSCTL_PATH, "get", "port", name, "interfaces"] + res = do_cmd(verify_port) + # Expecting python-style list as output + iface_list = [] + if len(res) > 2: + iface_list = res.strip()[1:-1].split(',') + if len(iface_list) != 1: + logging.debug("WARNING: Unexpected output while verifying " + + "port %s on bridge %s" % (name, bridge)) + return "FAILURE:VERIFY_PORT_FAILED" + + # verify interface + iface_uuid = iface_list[0] + verify_interface_key = [VSCTL_PATH, "get", "interface", + iface_uuid, "options:key"] + verify_interface_ip = [VSCTL_PATH, "get", "interface", + iface_uuid, "options:remote_ip"] + + key_validation = do_cmd(verify_interface_key) + ip_validation = do_cmd(verify_interface_ip) + + if not gre_key in key_validation or not remote_ip in ip_validation: + logging.debug("WARNING: Unexpected output while verifying " + + "interface %s on bridge %s" % (name, bridge)) + return "FAILURE:VERIFY_INTERFACE_FAILED" + logging.debug("Tunnel interface validated:%s" % verify_interface_ip) + cmd_tun_ofport = [VSCTL_PATH, "get", "interface", + iface_uuid, "ofport"] + tun_ofport = do_cmd(cmd_tun_ofport) + # Ensure no trailing LF + if tun_ofport.endswith('\n'): + tun_ofport = tun_ofport[:-1] + # find xs network for this bridge, verify is used for ovs tunnel network + xs_nw_uuid = do_cmd([XE_PATH, "network-list", + "bridge=%s" % bridge, "--minimal"]) + ovs_tunnel_network = False + try: + ovs_tunnel_network = do_cmd([XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=is-ovs-tun-network", "--minimal"]) + except: + pass + + ovs_vpc_distributed_vr_network = False + try: + ovs_vpc_distributed_vr_network = do_cmd([XE_PATH,"network-param-get", + "uuid=%s" % xs_nw_uuid, + "param-name=other-config", + "param-key=is-ovs-vpc-distributed-vr-network", "--minimal"]) + except: + pass + + if ovs_tunnel_network == 'True': + # add flow entryies for dropping broadcast coming in from gre tunnel + add_flow(bridge, priority=1000, in_port=tun_ofport, + dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') + add_flow(bridge, priority=1000, in_port=tun_ofport, + nw_dst='224.0.0.0/24', actions='drop') + drop_flow_setup = True + logging.debug("Broadcast drop rules added") + + if ovs_vpc_distributed_vr_network == 'True': + # add flow rules for dropping broadcast coming in from tunnel ports + add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') + add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, + nw_dst='224.0.0.0/24', actions='drop') + + # add flow rule to send the traffic from tunnel ports to L2 switching table only + add_flow(bridge, priority=1100, in_port=tun_ofport, table=0, actions='resubmit(,1)') + + # mark tunnel interface with network id for which this tunnel was created + do_cmd([VSCTL_PATH, "set", "interface", name, "options:cloudstack-network-id=%s" % network_uuid]) + update_flooding_rules_on_port_plug_unplug(bridge, name, 'online', network_uuid) + + logging.debug("Successfully created tunnel from host %s" %src_host + " to host %s" %dst_host + + " with GRE key %s" %gre_key) + return "SUCCESS:%s creation succeeded" % name + except: + logging.debug("An unexpected error occured. Rolling back") + if tunnel_setup: + logging.debug("Deleting GRE interface") + # Destroy GRE port and interface + del_port(bridge, name) + if drop_flow_setup: + # Delete flows + logging.debug("Deleting flow entries from GRE interface") + del_flows(bridge, in_port=tun_ofport) + # This will not cancel the original exception + raise + +# Configures the bridge created for a VPC that is enabled for distributed routing. Management server sends VPC +# physical topology details (which VM from which tier running on which host etc). Based on the VPC physical topology L2 +# lookup table and L3 lookup tables are updated by this function. def configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, sequence_no): vpconfig = jsonLoader(json.loads(json_config)).vpc @@ -412,8 +543,13 @@ def configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, network = get_network_details(vpconfig, nic.networkuuid) gre_key = network.grekey - # generate tunnel name as per the tunnel naming convention and get the OF port + # generate tunnel name as per the tunnel naming convention tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid) + + # check if tunnel exists already, if not create a tunnel from this host to remote host + if not check_tunnel_exists(bridge, tunnel_name): + create_tunnel(bridge, host.ipaddress, gre_key, this_host_id, host.hostid, network.networkuuid) + of_port = get_ofport_for_vif(tunnel_name) # Add flow rule in L2 look up table, if packet's destination mac matches MAC of the VM's nic @@ -441,10 +577,10 @@ def configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, del_flows(bridge, table=L3_LOOKUP_TABLE) ofspec.seek(0) - logging.debug("Adding below flows rules L2 & L3 lookup tables:\n" + ofspec.read()) + logging.debug("Adding below flows rules in L2 & L3 lookup tables:\n" + ofspec.read()) + ofspec.close() # update bridge with the flow-rules for L2 lookup and L3 lookup in the file in one attempt - ofspec.close() do_cmd([OFCTL_PATH, 'add-flows', bridge, ofspec_filename]) # now that we updated the bridge with flow rules close and delete the file. @@ -460,8 +596,9 @@ def configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, os.remove(ofspec_filename) raise error_message -# Configures the bridge created for a VPC enabled for distributed firewall. Management server sends VPC routing policies -# details. Based on the VPC routing policies ingress ACL table and egress ACL tables are updated by this function. +# Configures the bridge created for a VPC that is enabled for distributed firewall. Management server sends VPC routing +# policy (network ACL applied on the tiers etc) details. Based on the VPC routing policies ingress ACL table and +# egress ACL tables are updated by this function. def configure_vpc_bridge_for_routing_policies(bridge, json_config, sequence_no): vpconfig = jsonLoader(json.loads(json_config)).vpc @@ -564,9 +701,9 @@ def configure_vpc_bridge_for_routing_policies(bridge, json_config, sequence_no): ofspec.seek(0) logging.debug("Adding below flows rules Ingress & Egress ACL tables:\n" + ofspec.read()) + ofspec.close() # update bridge with the flow-rules for ingress and egress ACL's added in the file in one attempt - ofspec.close() do_cmd([OFCTL_PATH, 'add-flows', bridge, ofspec_filename]) # now that we updated the bridge with flow rules delete the file. @@ -658,9 +795,9 @@ def update_flooding_rules_on_port_plug_unplug(bridge, interface, command, if_net ofspec.seek(0) logging.debug("Adding below flows rules L2 flooding table: \n" + ofspec.read()) + ofspec.close() # update bridge with the flow-rules for broadcast rules added in the file in one attempt - ofspec.close() do_cmd([OFCTL_PATH, 'add-flows', bridge, ofspec_filename]) # now that we updated the bridge with flow rules delete the file. diff --git a/scripts/vm/hypervisor/xenserver/ovstunnel b/scripts/vm/hypervisor/xenserver/ovstunnel index 358cd5493b8..c95fc207211 100755 --- a/scripts/vm/hypervisor/xenserver/ovstunnel +++ b/scripts/vm/hypervisor/xenserver/ovstunnel @@ -193,7 +193,8 @@ def setup_ovs_bridge_for_distributed_routing(session, args): lib.add_flow(bridge, priority=0, table=lib.INGRESS_ACL_TABLE, actions='drop') # initialize the sequence number for the bridge - lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:sequence-number=0"]) + lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:topology-update-sequence-number=0"]) + lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:route-policy-update-sequence-number=0"]) result = "SUCCESS: successfully setup bridge with flow rules" @@ -232,128 +233,7 @@ def create_tunnel(session, args): dst_host = args.pop("to") network_uuid = args.pop("cloudstack-network-id") - logging.debug("Entering create_tunnel") - - res = lib.check_switch() - if res != "SUCCESS": - logging.debug("Openvswitch running: NO") - return "FAILURE:%s" % res - - # We need to keep the name below 14 characters - # src and target are enough - consider a fixed length hash - name = "t%s-%s-%s" % (gre_key, src_host, dst_host) - - # Verify the xapi bridge to be created - # NOTE: Timeout should not be necessary anymore - wait = [lib.VSCTL_PATH, "--timeout=30", "wait-until", "bridge", - bridge, "--", "get", "bridge", bridge, "name"] - res = lib.do_cmd(wait) - if bridge not in res: - logging.debug("WARNING:Can't find bridge %s for creating " + - "tunnel!" % bridge) - return "FAILURE:NO_BRIDGE" - logging.debug("bridge %s for creating tunnel - VERIFIED" % bridge) - tunnel_setup = False - drop_flow_setup = False - try: - # Create a port and configure the tunnel interface for it - add_tunnel = [lib.VSCTL_PATH, "add-port", bridge, - name, "--", "set", "interface", - name, "type=gre", "options:key=%s" % gre_key, - "options:remote_ip=%s" % remote_ip] - lib.do_cmd(add_tunnel) - tunnel_setup = True - # verify port - verify_port = [lib.VSCTL_PATH, "get", "port", name, "interfaces"] - res = lib.do_cmd(verify_port) - # Expecting python-style list as output - iface_list = [] - if len(res) > 2: - iface_list = res.strip()[1:-1].split(',') - if len(iface_list) != 1: - logging.debug("WARNING: Unexpected output while verifying " + - "port %s on bridge %s" % (name, bridge)) - return "FAILURE:VERIFY_PORT_FAILED" - - # verify interface - iface_uuid = iface_list[0] - verify_interface_key = [lib.VSCTL_PATH, "get", "interface", - iface_uuid, "options:key"] - verify_interface_ip = [lib.VSCTL_PATH, "get", "interface", - iface_uuid, "options:remote_ip"] - - key_validation = lib.do_cmd(verify_interface_key) - ip_validation = lib.do_cmd(verify_interface_ip) - - if not gre_key in key_validation or not remote_ip in ip_validation: - logging.debug("WARNING: Unexpected output while verifying " + - "interface %s on bridge %s" % (name, bridge)) - return "FAILURE:VERIFY_INTERFACE_FAILED" - logging.debug("Tunnel interface validated:%s" % verify_interface_ip) - cmd_tun_ofport = [lib.VSCTL_PATH, "get", "interface", - iface_uuid, "ofport"] - tun_ofport = lib.do_cmd(cmd_tun_ofport) - # Ensure no trailing LF - if tun_ofport.endswith('\n'): - tun_ofport = tun_ofport[:-1] - # find xs network for this bridge, verify is used for ovs tunnel network - xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list", - "bridge=%s" % bridge, "--minimal"]) - ovs_tunnel_network = False - try: - ovs_tunnel_network = lib.do_cmd([lib.XE_PATH,"network-param-get", - "uuid=%s" % xs_nw_uuid, - "param-name=other-config", - "param-key=is-ovs-tun-network", "--minimal"]) - except: - pass - - ovs_vpc_distributed_vr_network = False - try: - ovs_vpc_distributed_vr_network = lib.do_cmd([lib.XE_PATH,"network-param-get", - "uuid=%s" % xs_nw_uuid, - "param-name=other-config", - "param-key=is-ovs-vpc-distributed-vr-network", "--minimal"]) - except: - pass - - if ovs_tunnel_network == 'True': - # add flow entryies for dropping broadcast coming in from gre tunnel - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, - dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, - nw_dst='224.0.0.0/24', actions='drop') - drop_flow_setup = True - logging.debug("Broadcast drop rules added") - - if ovs_vpc_distributed_vr_network == 'True': - # add flow rules for dropping broadcast coming in from tunnel ports - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, - dl_dst='ff:ff:ff:ff:ff:ff', actions='drop') - lib.add_flow(bridge, priority=1000, in_port=tun_ofport, table=0, - nw_dst='224.0.0.0/24', actions='drop') - - # add flow rule to send the traffic from tunnel ports to L2 switching table only - lib.add_flow(bridge, priority=1100, in_port=tun_ofport, table=0, actions='resubmit(,1)') - - # mark tunnel interface with network id for which this tunnel was created - lib.do_cmd([lib.VSCTL_PATH, "set", "interface", name, "options:cloudstack-network-id=%s" % network_uuid]) - lib.update_flooding_rules_on_port_plug_unplug(bridge, name, 'online', network_uuid) - - return "SUCCESS:%s" % name - except: - logging.debug("An unexpected error occured. Rolling back") - if tunnel_setup: - logging.debug("Deleting GRE interface") - # Destroy GRE port and interface - lib.del_port(bridge, name) - if drop_flow_setup: - # Delete flows - logging.debug("Deleting flow entries from GRE interface") - lib.del_flows(bridge, in_port=tun_ofport) - # This will not cancel the original exception - raise - + return lib.create_tunnel(bridge, remote_ip, gre_key, src_host, dst_host, network_uuid) @echo def destroy_tunnel(session, args): @@ -414,10 +294,11 @@ def configure_ovs_bridge_for_network_topology(session, args): sequence_no = args.pop("seq-no") # get the last update sequence number - last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, "other-config:sequence-number"]) + last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, "other-config:topology-update-sequence-number"]) last_seq_no = last_seq_no[1:-1] if long(sequence_no) > long(last_seq_no): - lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:sequence-number=%s"%sequence_no]) + lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, + "other-config:topology-update-sequence-number=%s"%sequence_no]) return lib.configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, sequence_no) else: return "SUCCESS: Ignoring the update with the sequence number %s" %sequence_no + " as there is already recent" \ @@ -430,10 +311,12 @@ def configure_ovs_bridge_for_routing_policies(session, args): sequence_no = args.pop("seq-no") # get the last update sequence number - last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, "other-config:sequence-number"]) + last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, + "other-config:route-policy-update-sequence-number"]) last_seq_no = last_seq_no[1:-1] if long(sequence_no) > long(last_seq_no): - lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:sequence-number=%s"%sequence_no]) + lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, + "other-config:route-policy-update-sequence-number=%s"%sequence_no]) return lib.configure_vpc_bridge_for_routing_policies(bridge, json_config, sequence_no) else: return "SUCCESS: Ignoring the update with the sequence number %s" %sequence_no + " as there is already recent" \ @@ -448,4 +331,4 @@ if __name__ == "__main__": "getLabel": getLabel, "setup_ovs_bridge_for_distributed_routing": setup_ovs_bridge_for_distributed_routing, "configure_ovs_bridge_for_network_topology": configure_ovs_bridge_for_network_topology, - "configure_ovs_bridge_for_routing_policies": configure_ovs_bridge_for_routing_policies}) + "configure_ovs_bridge_for_routing_policies": configure_ovs_bridge_for_routing_policies}) \ No newline at end of file diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index cca6e1fc1ff..8cb8221e613 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -1647,7 +1647,8 @@ INSERT INTO `cloud`.`configuration`(category, instance, component, name, value, CREATE TABLE `cloud`.`op_vpc_distributed_router_sequence_no` ( `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', `vpc_id` bigint unsigned NOT NULL COMMENT 'vpc id.', - `sequence_no` bigint unsigned COMMENT 'seq number to be sent to agent, uniquely identifies topology or routing policy updates', + `topology_update_sequence_no` bigint unsigned COMMENT 'sequence number to be sent to hypervisor, uniquely identifies a VPC topology update', + `routing_policy__update_sequence_no` bigint unsigned COMMENT 'sequence number to be sent to hypervisor, uniquely identifies a routing policy update', PRIMARY KEY (`id`), UNIQUE `u_op_vpc_distributed_router_sequence_no_vpc_id`(`vpc_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;