Shared VxLAN (Part3: tenant network l3)

1. What is the problem?
VLAN network has some restrictions that VxLAN network doesn't have.
For more flexible networking deployment, we consider supporting
cross-pod VxLAN network.

We are going to use shadow agent/port mechanism to synchronize VTEP
information and make cross-pod VxLAN networking available, as discussed
in the specification document[1].

With the previous parts[2, 3], we can create instances in the same
VxLAN network but in different pods. With the help of shadow ports,
tunnels are correctly created so instances can communicate with each
other. But we have a problem during the association of a floating ip
with an instance port because "setup_bottom_router" job will also
create shadow port for floating ip association.

2. What is the solution to the problem?
Let "setup_bottom_router" and "setup_shadow_ports" jobs call the
same method to create shadow ports and handle conflict in that method.

3. What the features need to be implemented to the Tricircle
to realize the solution?
This is the third patch for cross-pod VxLAN networking support, which
introduces the following changes:

(1) Both creating floating ip and booting instance in vxlan network
will create shadow port, so we leave shadow port deletion work to
central plugin, it will delete shadow port when deleting instance port

With this patch, floating ip binding to the port which is from cross-pod
VxLAN network is supported.

[1] https://review.openstack.org/#/c/429155/
[2] https://review.openstack.org/#/c/425128/
[3] https://review.openstack.org/#/c/425129/

Change-Id: I7ca3e124232baf265ec5a8ed3df0aca1303a2ff7
This commit is contained in:
zhiyuan_cai 2017-01-16 12:56:13 +08:00
parent e87e080281
commit b4f9798abf
5 changed files with 79 additions and 69 deletions

View File

@ -31,6 +31,12 @@ RT_ROUTER = 'router'
RT_NS_ROUTER = 'ns_router'
RT_SG = 'security_group'
REAL_SHADOW_TYPE_MAP = {
RT_NETWORK: RT_SD_NETWORK,
RT_SUBNET: RT_SD_SUBNET,
RT_PORT: RT_SD_PORT
}
# check whether the resource type is properly provisioned.
def is_valid_resource_type(resource_type):

View File

@ -310,19 +310,10 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def delete_network(self, context, network_id):
t_ctx = t_context.get_context_from_neutron_context(context)
try:
mappings = db_api.get_bottom_mappings_by_top_id(
t_ctx, network_id, t_constants.RT_NETWORK)
mappings.extend(db_api.get_bottom_mappings_by_top_id(
t_ctx, network_id, t_constants.RT_SD_NETWORK))
processed_pod_set = set()
for mapping in mappings:
region_name = mapping[0]['region_name']
if region_name in processed_pod_set:
continue
processed_pod_set.add(region_name)
bottom_network_id = mapping[1]
self._get_client(region_name).delete_networks(
for pod, bottom_network_id in (
self.helper.get_real_shadow_resource_iterator(
t_ctx, t_constants.RT_NETWORK, network_id)):
self._get_client(pod['region_name']).delete_networks(
t_ctx, bottom_network_id)
# we do not specify resource_type when deleting routing entries
# so if both "network" and "shadow_network" type entries exist
@ -334,7 +325,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
filters=[{'key': 'top_id', 'comparator': 'eq',
'value': network_id},
{'key': 'pod_id', 'comparator': 'eq',
'value': mapping[0]['pod_id']}])
'value': pod['pod_id']}])
except Exception:
raise
with t_ctx.session.begin():
@ -458,22 +449,14 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def delete_subnet(self, context, subnet_id):
t_ctx = t_context.get_context_from_neutron_context(context)
try:
mappings = db_api.get_bottom_mappings_by_top_id(
t_ctx, subnet_id, t_constants.RT_SUBNET)
mappings.extend(db_api.get_bottom_mappings_by_top_id(
t_ctx, subnet_id, t_constants.RT_SD_SUBNET))
processed_pod_set = set()
for mapping in mappings:
region_name = mapping[0]['region_name']
if region_name in processed_pod_set:
continue
processed_pod_set.add(region_name)
bottom_subnet_id = mapping[1]
for pod, bottom_subnet_id in (
self.helper.get_real_shadow_resource_iterator(
t_ctx, t_constants.RT_SUBNET, subnet_id)):
region_name = pod['region_name']
self._get_client(region_name).delete_subnets(
t_ctx, bottom_subnet_id)
interface_name = t_constants.interface_port_name % (
mapping[0]['region_name'], subnet_id)
region_name, subnet_id)
self._delete_pre_created_port(t_ctx, context, interface_name)
# we do not specify resource_type when deleting routing entries
# so if both "subnet" and "shadow_subnet" type entries exist in
@ -485,7 +468,7 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
filters=[{'key': 'top_id', 'comparator': 'eq',
'value': subnet_id},
{'key': 'pod_id', 'comparator': 'eq',
'value': mapping[0]['pod_id']}])
'value': pod['pod_id']}])
except Exception:
raise
dhcp_port_name = t_constants.dhcp_port_name % subnet_id
@ -730,13 +713,11 @@ class TricirclePlugin(db_base_plugin_v2.NeutronDbPluginV2,
# we use a "delete_server_port" job to delete the local ports.
if port.get('device_owner') not in NON_VM_PORT_TYPES:
try:
mappings = db_api.get_bottom_mappings_by_top_id(
t_ctx, port_id, t_constants.RT_PORT)
if mappings:
pod_id = mappings[0][0]['pod_id']
bottom_port_id = mappings[0][1]
for pod, bottom_port_id in (
self.helper.get_real_shadow_resource_iterator(
t_ctx, t_constants.RT_PORT, port_id)):
self.xjob_handler.delete_server_port(t_ctx, bottom_port_id,
pod_id)
pod['pod_id'])
except Exception:
raise
with t_ctx.session.begin():

View File

@ -748,3 +748,44 @@ class NetworkHelper(object):
agent_tunnel = profile[t_constants.PROFILE_TUNNEL_IP]
db_api.ensure_agent_exists(t_ctx, pod['pod_id'], agent_host,
agent_type, agent_tunnel)
def prepare_shadow_port(self, ctx, project_id, target_pod, net_id,
port_body, agent=None):
host = port_body['binding:host_id']
create_body = {
'port': {
'tenant_id': project_id,
'admin_state_up': True,
'name': t_constants.shadow_port_name % port_body['id'],
'network_id': net_id,
'fixed_ips': [{
'ip_address': port_body['fixed_ips'][0]['ip_address']}],
'device_owner': t_constants.DEVICE_OWNER_SHADOW,
'binding:host_id': host
}
}
if agent:
create_body['port'].update(
{'binding:profile': {
t_constants.PROFILE_AGENT_TYPE: agent['type'],
t_constants.PROFILE_TUNNEL_IP: agent['tunnel_ip']}})
_, sw_port_id = self.prepare_bottom_element(
ctx, project_id, target_pod, {'id': port_body['id']},
t_constants.RT_SD_PORT, create_body)
return sw_port_id
@staticmethod
def get_real_shadow_resource_iterator(t_ctx, res_type, res_id):
shadow_res_type = t_constants.REAL_SHADOW_TYPE_MAP[res_type]
mappings = db_api.get_bottom_mappings_by_top_id(
t_ctx, res_id, res_type)
mappings.extend(db_api.get_bottom_mappings_by_top_id(
t_ctx, res_id, shadow_res_type))
processed_pod_set = set()
for pod, bottom_res_id in mappings:
region_name = pod['region_name']
if region_name in processed_pod_set:
continue
processed_pod_set.add(region_name)
yield pod, bottom_res_id

View File

@ -3082,7 +3082,9 @@ class PluginTest(unittest.TestCase,
'fixed_ips': [
{'subnet_id': db_api.get_bottom_id_by_top_id_region_name(
t_ctx, t_subnet_id, 'pod_1', constants.RT_SUBNET),
'ip_address': '10.0.0.4'}]
'ip_address': '10.0.0.4'}],
'binding:host_id': 'host_1',
'binding:vif_type': 'ovs'
}
TOP_PORTS.append(t_port)
BOTTOM1_PORTS.append(b_port)

View File

@ -423,20 +423,16 @@ class XManager(PeriodicTasks):
# ip association purpose
t_int_net_id = t_int_port['network_id']
t_int_subnet_id = t_int_port['fixed_ips'][0]['subnet_id']
# TODO(zhiyuan) adapt shadow agent way to create shadow port
port_body = {
'port': {
'tenant_id': project_id,
'admin_state_up': True,
'name': constants.shadow_port_name % t_int_port['id'],
'network_id': t_int_net_id,
'fixed_ips': [{'ip_address': t_int_port[
'fixed_ips'][0]['ip_address']}]
}
}
self.helper.prepare_bottom_element(
ctx, project_id, b_ext_pod, t_int_port,
constants.RT_SD_PORT, port_body)
b_int_port = b_client.get_ports(ctx, b_int_port_id)
host = b_int_port['binding:host_id']
agent_type = self.helper.get_agent_type_by_vif(
b_int_port['binding:vif_type'])
agent = db_api.get_agent_by_host_type(ctx, host, agent_type)
self.helper.prepare_shadow_port(
ctx, project_id, b_ext_pod, t_int_net_id,
b_int_port, agent)
# create routing entries for shadow network and subnet so we
# can easily find them during central network and subnet
# deletion, create_resource_mapping will catch DBDuplicateEntry
@ -539,7 +535,8 @@ class XManager(PeriodicTasks):
q_constants.DEVICE_OWNER_DVR_INTERFACE,
q_constants.DEVICE_OWNER_ROUTER_SNAT,
q_constants.DEVICE_OWNER_ROUTER_GW,
q_constants.DEVICE_OWNER_DHCP]
q_constants.DEVICE_OWNER_DHCP,
constants.DEVICE_OWNER_SHADOW]
ew_attached_port_types = [q_constants.DEVICE_OWNER_ROUTER_INTF,
q_constants.DEVICE_OWNER_DVR_INTERFACE,
q_constants.DEVICE_OWNER_ROUTER_GW]
@ -992,31 +989,14 @@ class XManager(PeriodicTasks):
continue
agent_info_map[key] = agent
create_body = {
'port': {
'tenant_id': project_id,
'admin_state_up': True,
'name': constants.shadow_port_name % port_id,
'network_id': t_net_id,
'fixed_ips': [{
'ip_address': port_body[
'fixed_ips'][0]['ip_address']}],
'device_owner': constants.DEVICE_OWNER_SHADOW,
'binding:host_id': host,
'binding:profile': {
constants.PROFILE_AGENT_TYPE: agent_type,
constants.PROFILE_TUNNEL_IP: agent['tunnel_ip']}
}
}
sw_port_id = self.helper.prepare_shadow_port(
ctx, project_id, target_pod, t_net_id, port_body, agent)
# value for key constants.PROFILE_FORCE_UP does not matter
update_body = {
'port': {
'binding:profile': {constants.PROFILE_FORCE_UP: 'True'}
}
}
_, sw_port_id = self.helper.prepare_bottom_element(
ctx, project_id, target_pod, {'id': port_id},
constants.RT_SD_PORT, create_body)
self._get_client(target_pod['region_name']).update_ports(
ctx, sw_port_id, update_body)