Add fullstack test for snat
Change-Id: I5fdaf510c9653c4fc57ee0529e575ded82c099e3 Closes-Bug: 1651988
This commit is contained in:
parent
9029522f40
commit
ebe78138bd
@ -21,6 +21,8 @@ export OVERRIDE_ENABLED_SERVICES=key,n-api,n-api-meta,n-cpu,n-cond,n-sch,n-crt,n
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_REDIS_PUBSUB=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_RUNNING_IN_GATE=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"EXTERNAL_HOST_IP=172.24.4.100"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"OVS_INSTALL_FROM_GIT=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"OVS_BRANCH=v2.7.1"
|
||||
|
||||
if [ -n "${DEVSTACK_GATE_TEMPEST}" ] && [ ${DEVSTACK_GATE_TEMPEST} -gt 0 ]; then
|
||||
# Only include tempest if this is a tempest job
|
||||
|
@ -25,6 +25,8 @@ export DEVSTACK_LOCAL_CONFIG+=$'\n'"ENABLE_ACTIVE_DETECTION=False"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_RUNNING_IN_GATE=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"EXTERNAL_HOST_IP=172.24.4.100"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"ENABLE_DF_SFC=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"OVS_INSTALL_FROM_GIT=True"
|
||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"OVS_BRANCH=v2.7.1"
|
||||
|
||||
if [ -n "${DEVSTACK_GATE_TEMPEST}" ] && [ ${DEVSTACK_GATE_TEMPEST} -gt 0 ]; then
|
||||
# Only include tempest if this is a tempest job
|
||||
|
@ -69,7 +69,7 @@ function _neutron_ovs_install_ovs_fedora {
|
||||
}
|
||||
|
||||
function _neutron_ovs_install_ovs_deps_ubuntu {
|
||||
install_package -y build-essential fakeroot devscripts equivs dkms
|
||||
install_package -y build-essential fakeroot devscripts equivs dkms linux-libc-dev linux-headers-$(uname -r)
|
||||
sudo mk-build-deps -i -t "/usr/bin/apt-get --no-install-recommends -y"
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ class ChassisSNATApp(df_base_app.DFlowApp, snat_mixin.SNATApp_mixin):
|
||||
LOG.info("Loading SNAT application ... ")
|
||||
self.external_network_bridge = (
|
||||
cfg.CONF.df_snat_app.external_network_bridge)
|
||||
self.external_bridge_mac = self.vswitch_api.get_port_mac_in_use(
|
||||
self.external_network_bridge) or const.EMPTY_MAC
|
||||
self.chassis = None
|
||||
|
||||
# new application configuration
|
||||
@ -58,7 +60,7 @@ class ChassisSNATApp(df_base_app.DFlowApp, snat_mixin.SNATApp_mixin):
|
||||
def switch_features_handler(self, ev):
|
||||
self._setup_patch_ports()
|
||||
self.external_bridge_mac = self.vswitch_api.get_port_mac_in_use(
|
||||
self.external_network_bridge)
|
||||
self.external_network_bridge) or const.EMPTY_MAC
|
||||
|
||||
# install static strategy flows
|
||||
if self.external_host_ip is None:
|
||||
|
@ -83,12 +83,18 @@ class SNATApp_mixin(object):
|
||||
def _install_ingress_goto_rules(self):
|
||||
parser = self.parser
|
||||
match = parser.OFPMatch(in_port=self.external_ofport)
|
||||
actions = [parser.NXActionRegLoad(
|
||||
dst='in_port',
|
||||
value=0,
|
||||
ofs_nbits=nicira_ext.ofs_nbits(0, 31))]
|
||||
|
||||
self.add_flow_go_to_table(
|
||||
const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
||||
const.PRIORITY_DEFAULT,
|
||||
const.INGRESS_NAT_TABLE,
|
||||
match=match)
|
||||
inst = [parser.OFPInstructionActions(
|
||||
self.datapath.ofproto.OFPIT_APPLY_ACTIONS, actions),
|
||||
parser.OFPInstructionGotoTable(const.INGRESS_NAT_TABLE)]
|
||||
self.mod_flow(inst=inst,
|
||||
table_id=const.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
||||
priority=const.PRIORITY_DEFAULT,
|
||||
match=match)
|
||||
|
||||
def _install_snat_egress_conntrack(self, match, ext_host_ip):
|
||||
"""implements single sNAT pass for multiple tenant deployment
|
||||
@ -162,16 +168,12 @@ class SNATApp_mixin(object):
|
||||
|
||||
actions = [
|
||||
parser.OFPActionSetField(eth_src=ext_host_mac),
|
||||
parser.OFPActionSetField(eth_dst=self.external_bridge_mac)
|
||||
]
|
||||
|
||||
action_inst = parser.OFPInstructionActions(
|
||||
ofproto.OFPIT_APPLY_ACTIONS, actions)
|
||||
goto_inst = parser.OFPInstructionGotoTable(const.EGRESS_EXTERNAL_TABLE)
|
||||
inst = [action_inst, goto_inst]
|
||||
parser.OFPActionSetField(eth_dst=self.external_bridge_mac),
|
||||
parser.OFPActionOutput(self.external_ofport,
|
||||
ofproto.OFPCML_NO_BUFFER)]
|
||||
|
||||
self.mod_flow(
|
||||
inst=inst,
|
||||
actions=actions,
|
||||
table_id=const.EGRESS_SNAT_TABLE,
|
||||
priority=const.PRIORITY_LOW,
|
||||
match=match)
|
||||
@ -247,3 +249,10 @@ class SNATApp_mixin(object):
|
||||
table_id=const.INGRESS_SNAT_TABLE,
|
||||
priority=const.PRIORITY_LOW,
|
||||
match=match)
|
||||
|
||||
actions = [self.parser.OFPActionOutput(
|
||||
self.external_ofport, self.ofproto.OFPCML_NO_BUFFER)]
|
||||
self.mod_flow(
|
||||
actions=actions,
|
||||
table_id=const.INGRESS_DISPATCH_TABLE,
|
||||
priority=const.PRIORITY_DEFAULT)
|
||||
|
@ -16,6 +16,7 @@ import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
from neutron.agent.common import ip_lib
|
||||
from neutron.agent.common import utils
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_log import log
|
||||
@ -2661,3 +2662,142 @@ class TestTrunkApp(test_base.DFTestBase):
|
||||
result.add_protocol(icmp)
|
||||
result.serialize()
|
||||
return result.data
|
||||
|
||||
|
||||
class TestSNat(test_base.DFTestBase):
|
||||
namespace_name = 'test-snat'
|
||||
iface0_name = 'snat_veth0'
|
||||
iface1_name = 'snat_veth1'
|
||||
|
||||
def setUp(self):
|
||||
super(TestSNat, self).setUp()
|
||||
ipwrapper = ip_lib.IPWrapper()
|
||||
snat_veth0_device, snat_veth1_device = ipwrapper.add_veth(
|
||||
self.iface0_name,
|
||||
self.iface1_name, self.namespace_name)
|
||||
|
||||
snat_veth1_device.link.set_up()
|
||||
snat_veth1_device.addr.add('10.0.1.2/30')
|
||||
|
||||
snat_veth0_device.link.set_up()
|
||||
snat_veth0_device.addr.add('10.0.1.1/30')
|
||||
|
||||
snat_veth1_device.route.add_gateway('10.0.1.1')
|
||||
|
||||
time.sleep(10)
|
||||
|
||||
def tearDown(self):
|
||||
ipwrapper = ip_lib.IPWrapper()
|
||||
ipwrapper.del_veth(self.iface0_name)
|
||||
ipwrapper.netns.delete(self.namespace_name)
|
||||
super(TestSNat, self).tearDown()
|
||||
|
||||
def test_icmp_ping_pong_with_external_peer(self):
|
||||
self._create_topology()
|
||||
policy = self._create_policy()
|
||||
policy.start(self.topology)
|
||||
policy.wait(const.DEFAULT_RESOURCE_READY_TIMEOUT)
|
||||
if len(policy.exceptions) > 0:
|
||||
raise policy.exceptions[0]
|
||||
|
||||
def _create_topology(self):
|
||||
self.topology = self.store(
|
||||
app_testing_objects.Topology(
|
||||
self.neutron,
|
||||
self.nb_api
|
||||
)
|
||||
)
|
||||
self.subnet1 = self.topology.create_subnet(cidr='192.168.15.0/24')
|
||||
self.port1 = self.subnet1.create_port()
|
||||
self.router = self.topology.create_router([self.subnet1.subnet_id])
|
||||
self.topology.create_external_network([self.router.router_id])
|
||||
time.sleep(const.DEFAULT_RESOURCE_READY_TIMEOUT)
|
||||
|
||||
def _create_policy(self):
|
||||
port_policies = self._create_port_policies()
|
||||
initial_packet = self._create_packet(
|
||||
'10.0.1.2', ryu.lib.packet.ipv4.inet.IPPROTO_ICMP)
|
||||
policy = self.store(
|
||||
app_testing_objects.Policy(
|
||||
initial_actions=[
|
||||
app_testing_objects.SendAction(
|
||||
self.subnet1.subnet_id,
|
||||
self.port1.port_id,
|
||||
initial_packet
|
||||
),
|
||||
],
|
||||
port_policies=port_policies,
|
||||
unknown_port_action=app_testing_objects.IgnoreAction()
|
||||
)
|
||||
)
|
||||
return policy
|
||||
|
||||
def _create_port_policies(self):
|
||||
ignore_action = app_testing_objects.IgnoreAction()
|
||||
raise_action = app_testing_objects.RaiseAction("Unexpected packet")
|
||||
key1 = (self.subnet1.subnet_id, self.port1.port_id)
|
||||
actions = [app_testing_objects.DisableRuleAction(),
|
||||
app_testing_objects.StopSimulationAction()]
|
||||
rules1 = [
|
||||
app_testing_objects.PortPolicyRule(
|
||||
# Detect pong, end simulation
|
||||
app_testing_objects.RyuICMPPongFilter(self._get_ping),
|
||||
actions=actions
|
||||
),
|
||||
app_testing_objects.PortPolicyRule(
|
||||
# Ignore gratuitous ARP packets
|
||||
app_testing_objects.RyuARPGratuitousFilter(),
|
||||
actions=[
|
||||
ignore_action
|
||||
]
|
||||
),
|
||||
app_testing_objects.PortPolicyRule(
|
||||
# Ignore IPv6 packets
|
||||
app_testing_objects.RyuIPv6Filter(),
|
||||
actions=[
|
||||
ignore_action
|
||||
]
|
||||
),
|
||||
]
|
||||
policy1 = app_testing_objects.PortPolicy(
|
||||
rules=rules1,
|
||||
default_action=raise_action
|
||||
)
|
||||
return {
|
||||
key1: policy1,
|
||||
}
|
||||
|
||||
def _create_packet(self, dst_ip, proto, ttl=255):
|
||||
router_interface = self.router.router_interfaces[
|
||||
self.subnet1.subnet_id
|
||||
]
|
||||
router_interface_port = self.neutron.show_port(
|
||||
router_interface['port_id']
|
||||
)
|
||||
ethernet = ryu.lib.packet.ethernet.ethernet(
|
||||
src=self.port1.port.get_logical_port().mac,
|
||||
dst=router_interface_port['port']['mac_address'],
|
||||
ethertype=ryu.lib.packet.ethernet.ether.ETH_TYPE_IP,
|
||||
)
|
||||
ip = ryu.lib.packet.ipv4.ipv4(
|
||||
src=self.port1.port.get_logical_port().ip,
|
||||
dst=dst_ip,
|
||||
ttl=ttl,
|
||||
proto=proto,
|
||||
)
|
||||
ip_data = ryu.lib.packet.icmp.icmp(
|
||||
type_=ryu.lib.packet.icmp.ICMP_ECHO_REQUEST,
|
||||
data=ryu.lib.packet.icmp.echo(
|
||||
data=self._create_random_string())
|
||||
)
|
||||
self._ping = ip_data
|
||||
self._ip = ip
|
||||
result = ryu.lib.packet.packet.Packet()
|
||||
result.add_protocol(ethernet)
|
||||
result.add_protocol(ip)
|
||||
result.add_protocol(ip_data)
|
||||
result.serialize()
|
||||
return result.data
|
||||
|
||||
def _get_ping(self):
|
||||
return self._ping
|
||||
|
@ -39,11 +39,6 @@ class TestChassisSNATApp(test_app_base.DFAppTestBase):
|
||||
|
||||
self.SNAT_app.add_flow_go_to_table.assert_has_calls(
|
||||
[mock.call(
|
||||
constants.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
||||
constants.PRIORITY_DEFAULT,
|
||||
constants.INGRESS_NAT_TABLE,
|
||||
match=mock.ANY),
|
||||
mock.call(
|
||||
constants.L3_LOOKUP_TABLE,
|
||||
constants.PRIORITY_MEDIUM_LOW,
|
||||
constants.EGRESS_NAT_TABLE,
|
||||
@ -51,6 +46,11 @@ class TestChassisSNATApp(test_app_base.DFAppTestBase):
|
||||
|
||||
self.SNAT_app.mod_flow.assert_has_calls(
|
||||
[mock.call(
|
||||
inst=mock.ANY,
|
||||
table_id=constants.INGRESS_CLASSIFICATION_DISPATCH_TABLE,
|
||||
priority=constants.PRIORITY_DEFAULT,
|
||||
match=mock.ANY),
|
||||
mock.call(
|
||||
inst=mock.ANY,
|
||||
table_id=constants.INGRESS_NAT_TABLE,
|
||||
priority=constants.PRIORITY_LOW,
|
||||
@ -61,7 +61,7 @@ class TestChassisSNATApp(test_app_base.DFAppTestBase):
|
||||
priority=constants.PRIORITY_LOW,
|
||||
match=mock.ANY),
|
||||
mock.call(
|
||||
inst=mock.ANY,
|
||||
actions=mock.ANY,
|
||||
table_id=constants.EGRESS_SNAT_TABLE,
|
||||
priority=constants.PRIORITY_LOW,
|
||||
match=mock.ANY)])
|
||||
|
Loading…
Reference in New Issue
Block a user