Add NAT service instance test
Change-Id: Ib7e71232655ea4abc391336e739d287f5074aeed
This commit is contained in:
parent
84d5d7b7b1
commit
eb1dbe1873
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to create NAT service image from ubuntu image
|
||||||
|
|
||||||
|
guestfish << _EOF_
|
||||||
|
add $IMAGE
|
||||||
|
run
|
||||||
|
mount /dev/sda1 /
|
||||||
|
download /etc/sysctl.conf /tmp/sysctl.conf
|
||||||
|
! echo "net.ipv4.ip_forward = 1" >> /tmp/sysctl.conf
|
||||||
|
upload /tmp/sysctl.conf /etc/sysctl.conf
|
||||||
|
! echo "dhclient" > /tmp/rc.local
|
||||||
|
! echo "/sbin/iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE" >> /tmp/rc.local
|
||||||
|
! echo "/sbin/iptables -A FORWARD -i eth2 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT" >> /tmp/rc.local
|
||||||
|
! echo "/sbin/iptables -A FORWARD -i eth1 -o eth2 -j ACCEPT" >> /tmp/rc.local
|
||||||
|
! echo "exit 0" >> /tmp/rc.local
|
||||||
|
upload /tmp/rc.local /etc/rc.local
|
||||||
|
echo "Set root password to 'password'"
|
||||||
|
download /etc/shadow /tmp/shadow
|
||||||
|
! sed -i 's/^root:[^:]\+:/root:$1$h2thgSpv$lAm04bPzOoldW8H0EVwVA0:/' /tmp/shadow
|
||||||
|
upload /tmp/shadow /etc/shadow
|
||||||
|
echo "Permit root login"
|
||||||
|
download /etc/ssh/sshd_config /tmp/sshd_config
|
||||||
|
! sed -i 's/^PasswordAuthentication.*$/PasswordAuthentication yes/' /tmp/sshd_config
|
||||||
|
! sed -i 's/^PermitRootLogin.*$/PermitRootLogin yes/' /tmp/sshd_config
|
||||||
|
upload /tmp/sshd_config /etc/ssh/sshd_config
|
||||||
|
_EOF_
|
|
@ -10,12 +10,14 @@ from vapor.fixtures.contrail import * # noqa
|
||||||
from vapor.fixtures.contrail_resources import * # noqa
|
from vapor.fixtures.contrail_resources import * # noqa
|
||||||
from vapor.fixtures.different_tenants_resources import * # noqa
|
from vapor.fixtures.different_tenants_resources import * # noqa
|
||||||
from vapor.fixtures.dns import * # noqa
|
from vapor.fixtures.dns import * # noqa
|
||||||
|
from vapor.fixtures.images import * # noqa
|
||||||
from vapor.fixtures.instance_ip import * # noqa
|
from vapor.fixtures.instance_ip import * # noqa
|
||||||
from vapor.fixtures.ipams import * # noqa
|
from vapor.fixtures.ipams import * # noqa
|
||||||
from vapor.fixtures.networks import * # noqa
|
from vapor.fixtures.networks import * # noqa
|
||||||
from vapor.fixtures.nodes import * # noqa
|
from vapor.fixtures.nodes import * # noqa
|
||||||
from vapor.fixtures.policies import * # noqa
|
from vapor.fixtures.policies import * # noqa
|
||||||
from vapor.fixtures.security_groups import * # noqa
|
from vapor.fixtures.security_groups import * # noqa
|
||||||
|
from vapor.fixtures.service_chain import * # noqa
|
||||||
from vapor.fixtures.skip import * # noqa
|
from vapor.fixtures.skip import * # noqa
|
||||||
from vapor.fixtures.subnets import * # noqa
|
from vapor.fixtures.subnets import * # noqa
|
||||||
from vapor.fixtures.system_services import * # noqa
|
from vapor.fixtures.system_services import * # noqa
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from stepler.glance.fixtures import images
|
||||||
|
from stepler.third_party import utils
|
||||||
|
|
||||||
|
from vapor import settings
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def nat_service_image(get_glance_steps, uncleanable, credentials):
|
||||||
|
"""Session fixture to create ubuntu image.
|
||||||
|
Creates image from config.UBUNTU_QCOW2_URL with default options.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
get_glance_steps (function): function to get glance steps
|
||||||
|
uncleanable (AttrDict): data structure with skipped resources
|
||||||
|
credentials (object): CredentialsManager instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
object: ubuntu glance image
|
||||||
|
"""
|
||||||
|
with images.create_images_context(
|
||||||
|
get_glance_steps,
|
||||||
|
uncleanable,
|
||||||
|
credentials,
|
||||||
|
utils.generate_ids('nat'),
|
||||||
|
settings.NAT_SERVICE_IMAGE_URL) as created_images:
|
||||||
|
yield created_images[0]
|
|
@ -0,0 +1,82 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
import pycontrail.types as types
|
||||||
|
import pytest
|
||||||
|
from stepler import config as stepler_config
|
||||||
|
from stepler.third_party import utils
|
||||||
|
|
||||||
|
from vapor.helpers import service_chain
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def add_neutron_user_to_project(current_project, role_steps, user_steps):
|
||||||
|
"""Workaround to allow contrail to boot service instances."""
|
||||||
|
neutron_user = user_steps.get_user(name='neutron')
|
||||||
|
role = role_steps.get_role(name=stepler_config.ROLE_MEMBER)
|
||||||
|
role_steps.grant_role(role, neutron_user, project=current_project)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def service_template(contrail_api_client, nat_service_image, flavor):
|
||||||
|
name = next(utils.generate_ids('nat_template'))
|
||||||
|
interface_types = [
|
||||||
|
types.ServiceTemplateInterfaceType(
|
||||||
|
service_interface_type='management'),
|
||||||
|
types.ServiceTemplateInterfaceType(service_interface_type='left'),
|
||||||
|
types.ServiceTemplateInterfaceType(service_interface_type='right'),
|
||||||
|
]
|
||||||
|
template_properties = types.ServiceTemplateType(
|
||||||
|
version=1,
|
||||||
|
service_mode=u'in-network',
|
||||||
|
service_type=u'firewall',
|
||||||
|
image_name=nat_service_image.name,
|
||||||
|
flavor=flavor.name,
|
||||||
|
interface_type=interface_types,
|
||||||
|
ordered_interfaces=True,
|
||||||
|
service_virtualization_type=u'virtual-machine')
|
||||||
|
template = types.ServiceTemplate(
|
||||||
|
name=name, service_template_properties=template_properties)
|
||||||
|
contrail_api_client.service_template_create(template)
|
||||||
|
yield template
|
||||||
|
contrail_api_client.service_template_delete(id=template.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def service_instance(request, contrail_api_client, contrail_current_project,
|
||||||
|
service_template, contrail_2_networks, server_steps,
|
||||||
|
add_neutron_user_to_project):
|
||||||
|
left_vn, right_vn = contrail_2_networks.networks
|
||||||
|
left_fq_name = ':'.join(left_vn['contrail:fq_name'])
|
||||||
|
right_fq_name = ':'.join(right_vn['contrail:fq_name'])
|
||||||
|
name = next(utils.generate_ids('nat_instance'))
|
||||||
|
instance_properties = types.ServiceInstanceType(
|
||||||
|
scale_out=types.ServiceScaleOutType(1),
|
||||||
|
management_virtual_network='',
|
||||||
|
left_virtual_network=left_fq_name,
|
||||||
|
right_virtual_network=right_fq_name)
|
||||||
|
instance = types.ServiceInstance(
|
||||||
|
name=name,
|
||||||
|
parent_obj=contrail_current_project,
|
||||||
|
service_instance_properties=instance_properties)
|
||||||
|
instance.set_service_template(service_template)
|
||||||
|
contrail_api_client.service_instance_create(instance)
|
||||||
|
|
||||||
|
request.addfinalizer(
|
||||||
|
functools.partial(service_chain.delete_service_instance,
|
||||||
|
contrail_api_client, instance, server_steps))
|
||||||
|
|
||||||
|
service_chain.check_service_instance_ready(contrail_api_client, instance,
|
||||||
|
server_steps)
|
||||||
|
return instance
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from stepler import config as stepler_config
|
||||||
|
from stepler.third_party import waiter
|
||||||
|
|
||||||
|
from vapor import settings
|
||||||
|
|
||||||
|
|
||||||
|
def check_service_instance_ready(contrail_api_client, service_instance,
|
||||||
|
server_steps):
|
||||||
|
"""Check that service instance creates nova server and it's booted."""
|
||||||
|
|
||||||
|
def _get_virtual_machine_uuid():
|
||||||
|
fresh_instance = contrail_api_client.service_instance_read(
|
||||||
|
id=service_instance.uuid)
|
||||||
|
refs = fresh_instance.get_virtual_machine_back_refs()
|
||||||
|
if refs:
|
||||||
|
return refs[0]['uuid']
|
||||||
|
|
||||||
|
server_uuid = waiter.wait(
|
||||||
|
_get_virtual_machine_uuid,
|
||||||
|
timeout_seconds=settings.SERVICE_INSTANCE_CREATE_TIMEOUT)
|
||||||
|
|
||||||
|
server = next(server for server in server_steps.get_servers()
|
||||||
|
if server.id == server_uuid)
|
||||||
|
|
||||||
|
server_steps.check_server_status(
|
||||||
|
server,
|
||||||
|
expected_statuses=[stepler_config.STATUS_ACTIVE],
|
||||||
|
transit_statuses=[stepler_config.STATUS_BUILD],
|
||||||
|
timeout=stepler_config.SERVER_ACTIVE_TIMEOUT)
|
||||||
|
|
||||||
|
def _check_record_in_log():
|
||||||
|
server.get()
|
||||||
|
console = server.get_console_output()
|
||||||
|
return re.search(settings.SERVICE_INSTANCE_BOOT_DONE_PATTERN, console)
|
||||||
|
|
||||||
|
waiter.wait(
|
||||||
|
_check_record_in_log,
|
||||||
|
timeout_seconds=settings.SERVICE_INSTANCE_BOOT_TIMEOUT)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_service_instance(contrail_api_client, service_instance,
|
||||||
|
server_steps):
|
||||||
|
""""Delete service instance."""
|
||||||
|
refs = service_instance.get_virtual_machine_back_refs()
|
||||||
|
contrail_api_client.service_instance_delete(id=service_instance.uuid)
|
||||||
|
if refs:
|
||||||
|
server_uuid = refs[0]['uuid']
|
||||||
|
server = next(server for server in server_steps.get_servers()
|
||||||
|
if server.id == server_uuid)
|
||||||
|
server_steps.check_server_presence(
|
||||||
|
server,
|
||||||
|
present=False,
|
||||||
|
timeout=stepler_config.SERVER_DELETE_TIMEOUT)
|
|
@ -220,3 +220,13 @@ SECURITY_GROUP_SSH_PING_RULES = (stepler_config.SECURITY_GROUP_SSH_RULES +
|
||||||
SECURITY_GROUP_PING_RULES)
|
SECURITY_GROUP_PING_RULES)
|
||||||
|
|
||||||
DPDK_ENABLED_GROUP = u'Network devices using DPDK-compatible driver'
|
DPDK_ENABLED_GROUP = u'Network devices using DPDK-compatible driver'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Service chaining
|
||||||
|
# TODO(gdyuldin): relace with real URL
|
||||||
|
NAT_SERVICE_IMAGE_URL = os.environ.get('NAT_SERVICE_IMAGE_URL',
|
||||||
|
'/home/jenkins/nat.qcow2')
|
||||||
|
SERVICE_INSTANCE_CREATE_TIMEOUT = 2 * 60
|
||||||
|
SERVICE_INSTANCE_BOOT_TIMEOUT = 10 * 60
|
||||||
|
SERVICE_INSTANCE_BOOT_DONE_PATTERN = 'Cloud-init .+ finished'
|
|
@ -0,0 +1,156 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from hamcrest import assert_that, all_of, contains_string, is_not
|
||||||
|
import pycontrail.types as types
|
||||||
|
from stepler import config as stepler_config
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tcpdump_log_with_ping(server_steps, left_vm, right_vm, left_vm_fip,
|
||||||
|
right_vm_fip, right_vm_fixed_ip):
|
||||||
|
"""Start ping from left to right vm, returns right vm tcpdump's log."""
|
||||||
|
log_file = '/tmp/tcpdump'
|
||||||
|
with server_steps.get_server_ssh(
|
||||||
|
right_vm, right_vm_fip['floating_ip_address']) as right_ssh:
|
||||||
|
with right_ssh.sudo():
|
||||||
|
right_ssh.check_call('apt-get install -y tcpdump')
|
||||||
|
with server_steps.get_server_ssh(
|
||||||
|
left_vm, left_vm_fip['floating_ip_address']) as left_ssh:
|
||||||
|
with server_steps.check_ping_loss_context(
|
||||||
|
right_vm_fixed_ip, server_ssh=left_ssh):
|
||||||
|
tcpdump_pid = right_ssh.background_call(
|
||||||
|
'tcpdump -i eth0 icmp', stdout=log_file)
|
||||||
|
time.sleep(10)
|
||||||
|
right_ssh.check_call('kill {}'.format(tcpdump_pid))
|
||||||
|
return right_ssh.check_call('cat {}'.format(log_file)).stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_nat_service_instance(flavor, ubuntu_image, keypair, public_network,
|
||||||
|
neutron_security_group, contrail_2_networks,
|
||||||
|
create_floating_ip, contrail_network_policy,
|
||||||
|
set_network_policy, contrail_api_client,
|
||||||
|
server_steps, port_steps, service_instance):
|
||||||
|
"""Test contrail NAT service chain.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
#. Create flavor
|
||||||
|
#. Create ubuntu image
|
||||||
|
#. Create keypair
|
||||||
|
#. Create security group
|
||||||
|
#. Create left and right networks with subnets
|
||||||
|
#. Create service template
|
||||||
|
#. Create service instance with NAT service
|
||||||
|
#. Create policy
|
||||||
|
#. Assign policy to networks
|
||||||
|
#. Create left and right vm on corresponding networks
|
||||||
|
#. Start ping from left to right vm
|
||||||
|
#. Check that left vm ip is present on tcpdump output on right vm
|
||||||
|
#. Stop ping
|
||||||
|
#. Add service instance to policy rule
|
||||||
|
#. Start ping from left to right vm
|
||||||
|
#. Check that left vm ip is absent on tcpdump output on right vm and
|
||||||
|
right interface ip of service instance is present on tcpdump output
|
||||||
|
#. Stop ping
|
||||||
|
"""
|
||||||
|
# Add security group to NAT instance
|
||||||
|
nat_vm_id = service_instance.get_virtual_machine_back_refs()[0]['uuid']
|
||||||
|
nova_nat_instance = next(server for server in server_steps.get_servers()
|
||||||
|
if server.id == nat_vm_id)
|
||||||
|
nova_nat_instance.add_security_group(neutron_security_group['id'])
|
||||||
|
|
||||||
|
left_vn, right_vn = [
|
||||||
|
contrail_api_client.virtual_network_read(id=net['id'])
|
||||||
|
for net in contrail_2_networks.networks
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create policy rule with service
|
||||||
|
rule = types.PolicyRuleType(
|
||||||
|
action_list=types.ActionListType(simple_action='pass'),
|
||||||
|
direction='<>',
|
||||||
|
protocol='any',
|
||||||
|
src_addresses=[
|
||||||
|
types.AddressType(virtual_network=left_vn.get_fq_name_str())
|
||||||
|
],
|
||||||
|
src_ports=[types.PortType()],
|
||||||
|
dst_addresses=[
|
||||||
|
types.AddressType(virtual_network=right_vn.get_fq_name_str())
|
||||||
|
],
|
||||||
|
dst_ports=[types.PortType()])
|
||||||
|
contrail_network_policy.set_network_policy_entries(
|
||||||
|
types.PolicyEntriesType(policy_rule=[rule]))
|
||||||
|
contrail_api_client.network_policy_update(contrail_network_policy)
|
||||||
|
|
||||||
|
# Assign policy to networks
|
||||||
|
set_network_policy(left_vn, contrail_network_policy)
|
||||||
|
set_network_policy(right_vn, contrail_network_policy)
|
||||||
|
|
||||||
|
# Create 2 servers
|
||||||
|
servers = []
|
||||||
|
floating_ips = []
|
||||||
|
for net in contrail_2_networks.networks:
|
||||||
|
server = server_steps.create_servers(
|
||||||
|
flavor=flavor,
|
||||||
|
image=ubuntu_image,
|
||||||
|
networks=[net],
|
||||||
|
security_groups=[neutron_security_group],
|
||||||
|
keypair=keypair,
|
||||||
|
username=stepler_config.UBUNTU_USERNAME)[0]
|
||||||
|
|
||||||
|
servers.append(server)
|
||||||
|
|
||||||
|
server_port = port_steps.get_port(
|
||||||
|
device_owner=stepler_config.PORT_DEVICE_OWNER_SERVER,
|
||||||
|
device_id=server.id)
|
||||||
|
floating_ip = create_floating_ip(public_network, port=server_port)
|
||||||
|
|
||||||
|
floating_ips.append(floating_ip)
|
||||||
|
|
||||||
|
left_vm, right_vm = servers
|
||||||
|
left_vm_fip, right_vm_fip = floating_ips
|
||||||
|
|
||||||
|
# Install tcpdump on right vm
|
||||||
|
with server_steps.get_server_ssh(
|
||||||
|
right_vm, right_vm_fip['floating_ip_address']) as right_ssh:
|
||||||
|
with right_ssh.sudo():
|
||||||
|
right_ssh.check_call('apt-get install -y tcpdump')
|
||||||
|
|
||||||
|
right_vm_fixed_ip = server_steps.get_fixed_ip(right_vm)
|
||||||
|
nat_right_ip = nova_nat_instance.addresses[right_vn.name][0]['addr']
|
||||||
|
left_vm_fixed_ip = server_steps.get_fixed_ip(left_vm)
|
||||||
|
|
||||||
|
# Start ping from left to right server and tcpdump on right server
|
||||||
|
tcpdump_stdout = _get_tcpdump_log_with_ping(
|
||||||
|
server_steps, left_vm, right_vm, left_vm_fip, right_vm_fip,
|
||||||
|
right_vm_fixed_ip)
|
||||||
|
|
||||||
|
# Check that ICMP packets contains left vm ip address
|
||||||
|
assert_that(tcpdump_stdout, contains_string(left_vm_fixed_ip))
|
||||||
|
|
||||||
|
# Update policy rule
|
||||||
|
rule.action_list.apply_service = [service_instance.get_fq_name_str()]
|
||||||
|
contrail_network_policy.set_network_policy_entries(
|
||||||
|
types.PolicyEntriesType(policy_rule=[rule]))
|
||||||
|
contrail_api_client.network_policy_update(contrail_network_policy)
|
||||||
|
|
||||||
|
# Start ping from left to right server and tcpdump on right server
|
||||||
|
tcpdump_stdout = _get_tcpdump_log_with_ping(
|
||||||
|
server_steps, left_vm, right_vm, left_vm_fip, right_vm_fip,
|
||||||
|
right_vm_fixed_ip)
|
||||||
|
|
||||||
|
# Check that ICMP packets has NAT right ip address and no contains left
|
||||||
|
# vm ip address
|
||||||
|
assert_that(tcpdump_stdout,
|
||||||
|
all_of(
|
||||||
|
contains_string(nat_right_ip),
|
||||||
|
is_not(contains_string(left_vm_fixed_ip))))
|
Loading…
Reference in New Issue