Single/Multinode VPNaaS Scenario Tests using Rally

(a) Refactored the code to work with the following cases
    1. Single node with DVR
    2. Single node without DVR
    3. Multinode with DVR
    4. Multinode without DVR
|-------------|-------------------------------------------------------|
|             |                Single Node/ Multinode Tests           |
| Scenarios   |-------------------------------------------------------|
|             | DVR | DVR+Concurrency | Non DVR | Non DVR+Concurrency |
|-------------|-----|-----------------|---------|---------------------|
| Vpn Basic   |  x  |      x          |    x    |         x           |
| Multi Tenant|  x  |      x          |    x    |         x           |
| Vpn Status  |  x  |      x          |    x    |         x           |
|-------------|-----|-----------------|---------|---------------------|

(b) Used the paramiko package to execute commands over ssh.

(c) Moved the README to devref

Depends-On: Id3f199d688d648f4618a6850d094e26ca6bb9a7f
Change-Id: I24d5a8435f06014fa9164bef518cac62bbb70ef9
This commit is contained in:
Aishwarya Thangappa 2016-02-11 14:31:32 -08:00
parent e6f643f2a1
commit 22205d681e
11 changed files with 982 additions and 539 deletions

View File

@ -34,7 +34,6 @@ VPNaaS Flavors
Info on the different Swan flavors, how they are different, and what
Operating Systems support them.
VPNaaS Internals
-----------------
.. toctree::
@ -42,6 +41,13 @@ VPNaaS Internals
multiple-local-subnets
VPNaaS Rally Tests
--------------------
.. toctree::
:maxdepth: 3
vpnaas-rally-test
Testing
-------
.. toctree::
@ -54,7 +60,6 @@ Testing
Add notes about functional testing, with info on how
different reference drivers are tested.
Module Reference
----------------
.. toctree::

View File

@ -0,0 +1,67 @@
===================
VPNaaS Rally Tests
===================
This contains the rally test codes for the Neutron VPN as a Service (VPNaaS) service. The tests
currently require rally to be installed via devstack or standalone. It is assumed that you
also have Neutron with the Neutron VPNaaS service installed.
These tests could also be run against a multinode openstack.
Please see /neutron-vpnaas/devstack/README.md for the required devstack configuration settings
for Neutron-VPNaaS.
Structure:
==========
1. plugins - Directory where you can add rally plugins. Almost everything in Rally is a plugin.
Contains base, common methods and actual scenario tests
2. rally-configs - Contains input configurations for the scenario tests
How to test:
============
Included in the repo are rally tests. For information on rally, please see the rally README :
https://github.com/openstack/rally/blob/master/README.rst
* Create a rally deployment for your cloud and make sure it is active.
rally deployment create --file=cloud_cred.json --name=MyCloud
You can also create a rally deployment from the environment variables.
rally deployment create --fromenv --name=MyCloud
* Create a folder structure as below
sudo mkdir /opt/rally
* Create a symbolic link to the plugins directory
cd /opt/rally
sudo ln -s /opt/stack/neutron-vpnaas/rally-jobs/plugins
* Run the tests. You can run the tests in various combinations.
(a) Single Node with DVR with admin credentials
(b) Single Node with DVR with non admin credentials
(c) Multi Node with DVR with admin credentials
(d) Multi Node with DVR with non admin credentials
(e) Single Node, Non DVR with admin credentials
(f) Multi Node, Non DVR with admin credentials
-> Create a args.json file with the correct credentials depending on whether it is a
single node or multinode cloud. A args_template.json file is available at
/opt/stack/neutron-vpnaas/rally-jobs/rally-configs/args_template.json for your reference.
-> Update the rally_config_dvr.yaml or rally_config_non_dvr.yaml file to change the
admin/non_admin credentials.
-> Use the appropriate config files to run either dvr or non_dvr tests.
With DVR:
rally task start /opt/stack/neutron-vpnaas/rally-jobs/rally-configs/rally_config_dvr.yaml
--task-args-file /opt/stack/neutron-vpnaas/rally-jobs/rally-configs/args.json
Non DVR:
rally task start /opt/stack/neutron-vpnaas/rally-jobs/rally-configs/rally_config_non_dvr.yaml
--task-args-file /opt/stack/neutron-vpnaas/rally-jobs/rally-configs/args.json
**Note:**
Non DVR scenario can only be run as admin as you need admin credentials to create
a non DVR router.
External Resources:
===================
For more information on the rally testing framework see: <https://github.com/openstack/rally>

View File

@ -1,36 +0,0 @@
Welcome!
========
This contains rally testing code for the Neutron VPN as a Service (VPNaaS) service. The tests
currently require rally to be installed via devstack or standalone. It is assumed that you
also have Neutron with the Neutron VPNaaS service installed.
Please see /neutron-vpnaas/devstack/README.md for the required devstack configuration settings
for Neutron-VPNaaS.
Structure:
==========
1. plugins - Directory where you can add rally plugins. Almost everything in Rally is a plugin.
Contains base, common methods and actual scenario tests
2. rally-configs - Contains input config for the scenario tests
How to test:
============
Included in the repo are rally tests. For information on rally, please see the rally README :
https://github.com/openstack/rally/blob/master/README.rst
* Create the folder structure as below
$> sudo mkdir /opt/rally
* Create a symbolic link to the plugin
$> cd /opt/rally
$> sudo ln -s /opt/stack/neutron-vpnaas/rally-jobs/plugins
* Run the tests
$> rally task start /opt/stack/neutron-vpnaas/rally-jobs/rally-configs/rally_config.yaml
External Resources:
===================
For more information on the rally testing framework see: <https://github.com/openstack/rally>

View File

@ -14,22 +14,20 @@
from neutron_vpnaas._i18n import _LI
from rally.common import log as logging
from rally.task import scenario
from rally.task import types as types
import vpn_base
import vpn_base
LOG = logging.getLogger(__name__)
class VpnBasicScenario(vpn_base.VpnBase):
class TestVpnBasicScenario(vpn_base.VpnBase):
"""Rally scenarios for VPNaaS"""
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@scenario.configure()
def create_and_delete_vpn_connection(
self, **kwargs):
def create_and_delete_vpn_connection(self, **kwargs):
"""Basic VPN connectivity scenario.
1. Create 2 private networks, subnets and routers
@ -44,10 +42,10 @@ class VpnBasicScenario(vpn_base.VpnBase):
8. Create VPN service at each of the routers
9. Create IPSEC site connections at both endpoints
10. Verify that the ipsec-site-connection is ACTIVE (takes upto 30secs)
11. To verify the vpn connectivity, get into the first snat
11. To verify the vpn connectivity, get into the peer router's snat
namespace and start a tcpdump at the qg-xxxx interface
12. SSH into the second instance from the second qrouter namespace
and try to ping the first instance
12. SSH into the nova instance from the local qrouter namespace
and try to ping the nova instance on the peer network.
14. Verify that the captured packets are encapsulated and encrypted.
15. Verify the connectivity in the reverse direction following the
steps 11 through 13
@ -55,16 +53,17 @@ class VpnBasicScenario(vpn_base.VpnBase):
"""
try:
self.setup()
self.create_networks_and_servers(**kwargs)
self.setup(**kwargs)
self.create_networks(**kwargs)
self.create_servers(**kwargs)
self.check_route()
self.ike_policy = self._create_ike_policy(**kwargs)
self.ipsec_policy = self._create_ipsec_policy(**kwargs)
self.create_vpn_services()
self.create_ipsec_site_connections(**kwargs)
self.assert_statuses(final_status='ACTIVE', **kwargs)
self.assert_vpn_connectivity()
LOG.info(_LI("VPN CONNECTIVITY TEST PASSED!!"))
self.verify_vpn_connectivity(**kwargs)
LOG.info(_LI("VPN CONNECTIVITY TEST PASSED!"))
finally:
self.cleanup()

View File

@ -12,17 +12,21 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron_vpnaas._i18n import _LI
from rally.common import log as logging
from rally.task import scenario
from rally.task import types as types
import vpn_base
LOG = logging.getLogger(__name__)
class TestVPNStatusScenario(vpn_base.VpnBase):
class TestVpnStatusScenario(vpn_base.VpnBase):
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@scenario.configure()
def check_vpn_status(
self, **kwargs):
def check_vpn_status(self, **kwargs):
"""Test VPN's status correctly after bringing router's status to
DOWN and back to ACTIVE state
@ -45,8 +49,8 @@ class TestVPNStatusScenario(vpn_base.VpnBase):
"""
try:
self.setup()
self.create_networks_and_servers(**kwargs)
self.setup(**kwargs)
self.create_networks(**kwargs)
self.check_route()
self.ike_policy = self._create_ike_policy(**kwargs)
self.ipsec_policy = self._create_ipsec_policy(**kwargs)
@ -59,6 +63,7 @@ class TestVPNStatusScenario(vpn_base.VpnBase):
self.update_router(self.router_ids[0], admin_state_up=True)
self.update_router(self.router_ids[1], admin_state_up=True)
self.assert_statuses(final_status='ACTIVE', **kwargs)
LOG.info(_LI("VPN STATUS TEST PASSED!"))
finally:
self.cleanup()

View File

@ -12,21 +12,24 @@
# License for the specific language governing permissions and limitations
# under the License.
from neutron_vpnaas._i18n import _LI
from rally.common import log as logging
from rally.task import scenario
from rally.task import types as types
import vpn_base
LOG = logging.getLogger(__name__)
class VpnTenantScenario(vpn_base.VpnBase):
class TestVpnTenantScenario(vpn_base.VpnBase):
"""Rally scenarios for VPNaaS"""
@types.set(image=types.ImageResourceType,
flavor=types.FlavorResourceType)
@scenario.configure()
def multitenants_vpn_test(
self, **kwargs):
"""Basic VPN connectivity scenario.
def multitenants_vpn_test(self, **kwargs):
"""Test VPN connectivity under two different tenants.
1. Create 2 private networks with 2 different tenants, subnets, routers
2. Create public network, subnets and GW IPs on routers, if not present
@ -41,19 +44,19 @@ class VpnTenantScenario(vpn_base.VpnBase):
9. Create IPSEC site connections at both endpoints
10. Verify that the vpn-service and ipsec-site-connection are ACTIVE
11. Cleanup the resources that are setup for this test
"""
try:
self.setup(use_admin_client=True)
self.create_tenant()
self.create_networks_and_servers(**kwargs)
self.setup(**kwargs)
self.create_tenants()
self.create_networks(**kwargs)
self.check_route()
self.ike_policy = self._create_ike_policy(**kwargs)
self.ipsec_policy = self._create_ipsec_policy(**kwargs)
self.create_vpn_services()
self.create_ipsec_site_connections(**kwargs)
self.assert_statuses(final_status='ACTIVE', **kwargs)
LOG.info(_LI("VPN TENANT TEST PASSED!"))
finally:
self.cleanup()

View File

@ -14,8 +14,8 @@
import concurrent.futures
import exceptions
import multiprocessing
import re
import threading
import time
from oslo_utils import uuidutils
@ -26,68 +26,155 @@ from rally.task import atomic
import vpn_utils
LOG = logging.getLogger(__name__)
LOCK = multiprocessing.RLock()
LOCK = threading.RLock()
MAX_RESOURCES = 2
class VpnBase(rally_base.OpenStackScenario):
def setup(self, use_admin_client=False):
"""Creates and initializes data structures to hold various resources
:param use_admin_client: Use admin client when it is set to True
"""
self.tenant_ids = []
self.snat_namespaces = []
self.qrouter_namespaces = []
self.router_ids = []
self.rally_router_gw_ips = []
self.rally_routers = []
self.rally_networks = []
self.rally_subnets = []
self.rally_cidrs = []
self.ike_policy = None
self.ipsec_policy = None
self.vpn_services = []
self.ipsec_site_connections = []
self.servers = []
self.server_private_ips = []
self.suffixes = [uuidutils.generate_uuid(), uuidutils.generate_uuid()]
self.tenant_names = map(lambda x: "rally_tenant_" + x, self.suffixes)
self.key_names = map(lambda x: "rally_keypair_" + x, self.suffixes)
self.key_file_paths = map(lambda x: '/tmp/' + x, self.key_names)
self.nova_client = self.clients("nova")
self.neutron_client = self.clients("neutron")
self.neutron_admin_client = self.admin_clients("neutron")
if use_admin_client is True:
self.neutron_client = self.admin_clients("neutron")
self.keystone_client = self.admin_clients("keystone")
self.nova_client = self.admin_clients("nova")
def setup(self, **kwargs):
"""Create and initialize data structures to hold various resources"""
@atomic.action_timer("cleanup")
def cleanup(self):
"""Cleans up all the resources"""
with LOCK:
LOG.debug('SETUP RESOURCES')
self.neutron_admin_client = self.admin_clients("neutron")
if kwargs['use_admin_client']:
self.neutron_client = self.neutron_admin_client
self.keystone_client = self.admin_clients("keystone")
self.nova_client = self.admin_clients("nova")
else:
self.neutron_client = self.clients("neutron")
self.nova_client = self.clients("nova")
self.suffixes = [uuidutils.generate_uuid(),
uuidutils.generate_uuid()]
self.remote_key_files = ['rally_keypair_' + x
for x in self.suffixes]
self.local_key_files = ['/tmp/' + x for x in self.remote_key_files]
self.private_key_file = kwargs["private_key"]
self.keypairs = []
self.tenant_ids = []
self.ns_controller_tuples = []
self.qrouterns_compute_tuples = []
self.router_ids = []
self.rally_router_gw_ips = []
self.rally_routers = []
self.rally_networks = []
self.rally_subnets = []
self.rally_cidrs = []
self.ike_policy = None
self.ipsec_policy = None
self.vpn_services = []
self.ipsec_site_connections = []
self.servers = []
self.server_private_ips = []
self.server_fips = []
vpn_utils.delete_servers(self.nova_client, self.servers)
vpn_utils.delete_hosts_from_knownhosts_file(self.server_private_ips)
vpn_utils.delete_key_files(self.key_file_paths)
self._delete_ipsec_site_connections()
self._delete_vpn_services()
self._delete_ipsec_policy()
self._delete_ike_policy()
vpn_utils.delete_network(
self.neutron_client, self.neutron_admin_client, self.rally_routers,
self.rally_networks, self.rally_subnets)
if self.tenant_ids:
vpn_utils.delete_tenant(self.keystone_client,
self.tenant_ids)
def create_tenants(self):
"""Create tenants"""
for x in range(MAX_RESOURCES):
tenant_id = vpn_utils.create_tenant(
self.keystone_client, self.suffixes[x])
with LOCK:
self.tenant_ids.append(tenant_id)
def create_networks(self, **kwargs):
"""Create networks to test vpn connectivity"""
for x in range(MAX_RESOURCES):
if self.tenant_ids:
router, network, subnet, cidr = vpn_utils.create_network(
self.neutron_client, self.neutron_admin_client,
self.suffixes[x], tenant_id=self.tenant_ids[x],
DVR_flag=kwargs["DVR_flag"],
ext_net_name=kwargs["ext-net"])
else:
router, network, subnet, cidr = vpn_utils.create_network(
self.neutron_client, self.neutron_admin_client,
self.suffixes[x], DVR_flag=kwargs["DVR_flag"],
ext_net_name=kwargs["ext-net"])
with LOCK:
self.rally_cidrs.append(cidr)
self.rally_subnets.append(subnet)
self.rally_networks.append(network)
self.rally_routers.append(router)
self.router_ids.append(router["router"]['id'])
self.rally_router_gw_ips.append(
router["router"]["external_gateway_info"]
["external_fixed_ips"][0]["ip_address"])
if(kwargs["DVR_flag"]):
ns, controller = vpn_utils.wait_for_namespace_creation(
"snat-", router["router"]['id'],
kwargs['controller_creds'],
self.private_key_file,
kwargs['namespace_creation_timeout'])
else:
ns, controller = vpn_utils.wait_for_namespace_creation(
"qrouter-", router["router"]['id'],
kwargs['controller_creds'],
self.private_key_file,
kwargs['namespace_creation_timeout'])
with LOCK:
self.ns_controller_tuples.append((ns, controller))
def create_servers(self, **kwargs):
"""Create servers"""
for x in range(MAX_RESOURCES):
kwargs.update({
"nics":
[{"net-id": self.rally_networks[x]["network"]["id"]}],
"sec_group_suffix": self.suffixes[x],
"server_suffix": self.suffixes[x]
})
keypair = vpn_utils.create_keypair(
self.nova_client, self.suffixes[x])
server = vpn_utils.create_server(
self.nova_client, keypair, **kwargs)
vpn_utils.assert_server_status(server, **kwargs)
with LOCK:
self.servers.append(server)
self.keypairs.append(keypair)
self.server_private_ips.append(vpn_utils.get_server_ip(
self.nova_client, server.id, self.suffixes[x]))
if(kwargs["DVR_flag"]):
qrouter, compute = vpn_utils.wait_for_namespace_creation(
"qrouter-", self.router_ids[x],
kwargs['compute_creds'],
self.private_key_file,
kwargs['namespace_creation_timeout'])
vpn_utils.write_key_to_compute_node(
keypair, self.local_key_files[x],
self.remote_key_files[x], compute,
self.private_key_file)
with LOCK:
self.qrouterns_compute_tuples.append((qrouter, compute))
else:
vpn_utils.write_key_to_local_path(self.keypairs[x],
self.local_key_files[x])
fip = vpn_utils.add_floating_ip(self.nova_client, server)
with LOCK:
self.server_fips.append(fip)
def check_route(self):
"""Verify route exists between the router gateways"""
LOG.debug("VERIFY ROUTE EXISTS BETWEEN THE ROUTER GATEWAYS")
for tuple in self.ns_controller_tuples:
for ip in self.rally_router_gw_ips:
assert(vpn_utils.ping_router_gateway(
tuple, ip, self.private_key_file)), (
"PING TO IP " + ip + " FAILED")
@atomic.action_timer("_create_ike_policy")
def _create_ike_policy(self, **kwargs):
"""Creates IKE policy
"""Create IKE policy
:return: IKE policy
"""
LOG.debug("CREATING IKE_POLICY")
LOG.debug('CREATING IKE_POLICY')
ike_policy = self.neutron_client.create_ikepolicy({
"ikepolicy": {
"phase1_negotiation_mode":
@ -107,11 +194,11 @@ class VpnBase(rally_base.OpenStackScenario):
@atomic.action_timer("_create_ipsec_policy")
def _create_ipsec_policy(self, **kwargs):
"""Creates IPSEC policy
"""Create IPSEC policy
:return: IPSEC policy
"""
LOG.debug("CREATING IPSEC_POLICY")
LOG.debug('CREATING IPSEC_POLICY')
ipsec_policy = self.neutron_client.create_ipsecpolicy({
"ipsecpolicy": {
"name": "rally_ipsecpolicy",
@ -131,16 +218,15 @@ class VpnBase(rally_base.OpenStackScenario):
return ipsec_policy
@atomic.action_timer("_create_vpn_service")
def _create_vpn_service(self, rally_subnet,
rally_router, vpn_suffix=None):
"""Creates VPN service endpoints
def _create_vpn_service(self, rally_subnet, rally_router, vpn_suffix=None):
"""Create VPN service endpoints
:param rally_subnet: local subnet
:param rally_router: router endpoint
:param vpn_suffix: suffix name for vpn service
:return: VPN service
"""
LOG.debug("CREATING VPN_SERVICE")
LOG.debug('CREATING VPN_SERVICE')
vpn_service = self.neutron_client.create_vpnservice({
"vpnservice": {
"subnet_id": rally_subnet["subnet"]["id"],
@ -151,16 +237,24 @@ class VpnBase(rally_base.OpenStackScenario):
})
return vpn_service
def create_vpn_services(self):
"""Create VPN services"""
for x in range(MAX_RESOURCES):
vpn_service = self._create_vpn_service(
self.rally_subnets[x], self.rally_routers[x], self.suffixes[x])
with LOCK:
self.vpn_services.append(vpn_service)
@atomic.action_timer("_create_ipsec_site_connection")
def _create_ipsec_site_connection(self, local_index,
peer_index, **kwargs):
"""Creates IPSEC site connection
def _create_ipsec_site_connection(self, local_index, peer_index, **kwargs):
"""Create IPSEC site connection
:param local_index: parameter to point to the local end-point
:param peer_index: parameter to point to the peer end-point
:return: IPSEC site connection
"""
LOG.debug("CREATING IPSEC_SITE_CONNECTION")
LOG.debug('CREATING IPSEC_SITE_CONNECTION')
ipsec_site_conn = self.neutron_client.create_ipsec_site_connection({
"ipsec_site_connection": {
"psk": kwargs.get("secret", "secret"),
@ -185,26 +279,31 @@ class VpnBase(rally_base.OpenStackScenario):
})
return ipsec_site_conn
def create_ipsec_site_connections(self, **kwargs):
"""Create IPSEC site connections"""
a = self._create_ipsec_site_connection(0, 1, **kwargs)
b = self._create_ipsec_site_connection(1, 0, **kwargs)
with LOCK:
self.ipsec_site_connections = [a, b]
def _get_resource(self, resource_tag, resource_id):
"""Gets the resource(vpn_service or ipsec_site_connection)
"""Get the resource(vpn_service or ipsec_site_connection)
:param resource_tag: "vpnservice" or "ipsec_site_connection"
:param resource_id: id of the resource
:return: resource (vpn_service or ipsec_site_connection)
"""
if resource_tag == "vpnservice":
vpn_service = self.neutron_client.show_vpnservice(
resource_id)
vpn_service = self.neutron_client.show_vpnservice(resource_id)
if vpn_service:
return vpn_service
elif resource_tag == 'ipsec_site_connection':
ipsec_site_conn = \
self.neutron_client.show_ipsec_site_connection(
resource_id)
ipsec_site_conn = self.neutron_client.show_ipsec_site_connection(
resource_id)
if ipsec_site_conn:
return ipsec_site_conn
@atomic.action_timer("_wait_for_status_change")
def _wait_for_status_change(self, resource, resource_tag, final_status,
wait_timeout=60, check_interval=1):
"""Wait for resource's status change
@ -214,16 +313,18 @@ class VpnBase(rally_base.OpenStackScenario):
:param resource: resource whose status has to be checked
:param final_status: desired final status of the resource
:param resource_tag: to identify the resource as vpnservice or
ipsec_site_connection
ipsec_site_connection
:param wait_timeout: timeout value in seconds
:param check_interval: time to sleep before each check for the status
change
:return: resource
"""
LOG.debug('WAIT_FOR_%s_STATUS_CHANGE ', resource[resource_tag]['id'])
start_time = time.time()
while True:
resource = self._get_resource(resource_tag,
resource[resource_tag]['id'])
resource = self._get_resource(
resource_tag, resource[resource_tag]['id'])
current_status = resource[resource_tag]['status']
if current_status == final_status:
return resource
@ -231,9 +332,9 @@ class VpnBase(rally_base.OpenStackScenario):
if time.time() - start_time > wait_timeout:
raise exceptions.Exception(
"Timeout waiting for resource {} to change to {} status".
format(resource[resource_tag]['name'], final_status)
)
format(resource[resource_tag]['name'], final_status))
@atomic.action_timer("wait_time_for_status_change")
def _assert_statuses(self, ipsec_site_conn, vpn_service,
final_status, **kwargs):
"""Assert statuses of vpn_service and ipsec_site_connection
@ -258,207 +359,166 @@ class VpnBase(rally_base.OpenStackScenario):
check_interval=5)
LOG.debug("VPN SERVICE STATUS %s", vpn_service['vpnservice']['status'])
LOG.debug("IPSEC_SITE_CONNECTION STATUS: %s",
LOG.debug("IPSEC_SITE_CONNECTION STATUS %s",
ipsec_site_conn['ipsec_site_connection']['status'])
self._validate_status(vpn_service, ipsec_site_conn, final_status)
def assert_statuses(self, final_status, **kwargs):
"""Assert active statuses for VPN services and VPN connections
def _validate_status(self, vpn_service, ipsec_site_conn, final_status):
"""Validate the statuses of vpn_service, ipsec_site_connection and
evaluate the final_status
:param ipsec_site_conn: ipsec_site_connection of an instance
:param vpn_service: vpn_service of an instance
:param final_status: status of vpn and ipsec_site_connection instance
:param final_status: the final status you expect the resource to be in
"""
assert(final_status == vpn_service['vpnservice']['status']), (
"VPN SERVICE IS NOT IN %s STATE" % final_status)
assert(final_status == ipsec_site_conn['ipsec_site_connection']
['status']), ("THE IPSEC SITE CONNECTION IS NOT IN %s STATE"
% final_status)
LOG.debug("ASSERTING ACTIVE STATUSES FOR VPN-SERVICES AND "
"IPSEC-SITE-CONNECTIONS")
for x in range(MAX_RESOURCES):
self._assert_statuses(
self.ipsec_site_connections[x], self.vpn_services[x],
final_status, **kwargs)
def _get_qg_interface(self, peer_index):
"""Get the qg- interface
:param peer_index: parameter to point to the local end-point
:return: qg-interface
"""
qg = vpn_utils.get_interfaces(
self.ns_controller_tuples[peer_index],
self.private_key_file)
p = re.compile(r"qg-\w+-\w+")
for line in qg:
m = p.search(line)
if m:
return m.group()
return None
@atomic.action_timer("_verify_vpn_connection")
def _verify_vpn_connection(self, local_index, peer_index):
"""Verifies the vpn connectivity between the endpoints
def _verify_vpn_connectivity(self, local_index, peer_index, **kwargs):
"""Verify the vpn connectivity between the endpoints
Get the qg- interface from the snat namespace corresponding to the
peer router and start a tcp dump. Concurrently, SSH into the nova
instance on the local subnet from the qrouter namespace and try
to ping the nova instance on the peer subnet. Inspect the captured
packets to see if they are encrypted.
:param local_index: parameter to point to the local end-point
:param peer_index: parameter to point to the peer end-point
:return: True or False
:return: True if vpn connectivity test passes
False if the test fails
"""
qg = vpn_utils.get_interfaces(self.snat_namespaces[peer_index])
if qg:
p = re.compile(r"qg-\w+-\w+")
m = p.search(qg)
if m:
qg_interface = m.group()
else:
qg_interface = None
qg_interface = self._get_qg_interface(peer_index)
if qg_interface:
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as e:
tcpdump_future = e.submit(vpn_utils.start_tcpdump,
self.ns_controller_tuples[peer_index],
qg_interface, self.private_key_file)
if(kwargs["DVR_flag"]):
ssh_future = e.submit(
vpn_utils.ssh_and_ping_server,
self.server_private_ips[local_index],
self.server_private_ips[peer_index],
self.qrouterns_compute_tuples[local_index],
self.remote_key_files[local_index],
self.private_key_file)
else:
ssh_future = e.submit(
vpn_utils.ssh_and_ping_server_with_fip,
self.server_fips[local_index],
self.server_private_ips[peer_index],
self.local_key_files[local_index],
self.private_key_file)
if qg_interface:
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as e:
tcpdump_future = e.submit(vpn_utils.start_tcpdump,
self.snat_namespaces[peer_index],
qg_interface)
ssh_future = e.submit(vpn_utils.ssh_and_ping_server,
self.server_private_ips[local_index],
self.server_private_ips[peer_index],
self.qrouter_namespaces[local_index],
self.key_file_paths[local_index])
assert(ssh_future.result()), "SSH/Ping failed"
lines = tcpdump_future.result().split('\n')
for line in lines:
if 'ESP' in line:
return True
assert(ssh_future.result()), "SSH/Ping failed"
for line in tcpdump_future.result():
if 'ESP' in line:
return True
return False
def verify_vpn_connectivity(self, **kwargs):
"""Verify VPN connectivity"""
LOG.debug("VERIFY THE VPN CONNECTIVITY")
with LOCK:
assert(self._verify_vpn_connectivity(
0, 1, **kwargs)), "VPN CONNECTION FAILED"
with LOCK:
assert(self._verify_vpn_connectivity(
1, 0, **kwargs)), "VPN CONNECTION FAILED"
def update_router(self, router_id, admin_state_up=False):
"""Update router's admin_state_up field
:param router_id: uuid of the router
:param admin_state_up: True or False
"""
LOG.debug('UPDATE ROUTER')
router_args = {'router': {'admin_state_up': admin_state_up}}
self.neutron_client.update_router(router_id, router_args)
@atomic.action_timer("_delete_ipsec_site_connection")
def _delete_ipsec_site_connections(self):
"""Deletes IPSEC site connections"""
"""Delete IPSEC site connections"""
if self.ipsec_site_connections:
for site_conn in self.ipsec_site_connections:
if "rally" in (site_conn['ipsec_site_connection']['name']):
LOG.debug("DELETING IPSEC_SITE_CONNECTION %s",
site_conn['ipsec_site_connection']['id'])
self.neutron_client.delete_ipsec_site_connection(
site_conn['ipsec_site_connection']['id'])
for site_conn in self.ipsec_site_connections:
LOG.debug("DELETING IPSEC_SITE_CONNECTION %s",
site_conn['ipsec_site_connection']['id'])
self.neutron_client.delete_ipsec_site_connection(
site_conn['ipsec_site_connection']['id'])
@atomic.action_timer("_delete_vpn_service")
def _delete_vpn_services(self):
"""Deletes VPN service endpoints"""
"""Delete VPN service endpoints"""
if self.vpn_services:
for vpn_service in self.vpn_services:
if "rally" in vpn_service['vpnservice']['name']:
LOG.debug("DELETING VPN_SERVICE %s",
vpn_service['vpnservice']['id'])
self.neutron_client.delete_vpnservice(
vpn_service['vpnservice']['id'])
for vpn_service in self.vpn_services:
LOG.debug("DELETING VPN_SERVICE %s",
vpn_service['vpnservice']['id'])
self.neutron_client.delete_vpnservice(
vpn_service['vpnservice']['id'])
@atomic.action_timer("_delete_ipsec_policy")
def _delete_ipsec_policy(self):
"""Deletes IPSEC policy
"""Delete IPSEC policy"""
:param ipsec_policy: ipsec_policy object
:return:
"""
LOG.debug("DELETING IPSEC POLICY")
if (self.ipsec_policy and
"rally" in self.ipsec_policy['ipsecpolicy']['name']):
if self.ipsec_policy:
self.neutron_client.delete_ipsecpolicy(
self.ipsec_policy['ipsecpolicy']['id'])
@atomic.action_timer("_delete_ike_policy")
def _delete_ike_policy(self):
"""Deletes IKE policy
"""Delete IKE policy"""
:param ike_policy: ike_policy object
:return:
"""
LOG.debug("DELETING IKE POLICY")
if (self.ike_policy and
"rally" in self.ike_policy['ikepolicy']['name']):
LOG.debug('DELETING IKE POLICY')
if self.ike_policy:
self.neutron_client.delete_ikepolicy(
self.ike_policy['ikepolicy']['id'])
def create_tenant(self):
"""Creates tenant
@atomic.action_timer("cleanup")
def cleanup(self):
"""Clean the resources"""
:param keystone_client: keystone_admin_client
:param tenant_ids: append created tenant id into the list
:return:
"""
with LOCK:
for x in range(MAX_RESOURCES):
self.tenant_ids.append((vpn_utils.create_tenant(
self.keystone_client,
self.tenant_names[x])).id)
vpn_utils.delete_servers(self.nova_client, self.servers)
if self.server_fips:
vpn_utils.delete_floating_ips(self.nova_client, self.server_fips)
vpn_utils.delete_keypairs(self.nova_client, self.keypairs)
def create_networks_and_servers(self, **kwargs):
with LOCK:
keypairs = []
for x in range(MAX_RESOURCES):
if self.tenant_ids:
router, network, subnet, cidr = vpn_utils.create_network(
self.neutron_client, self.neutron_admin_client,
self.suffixes[x], self.tenant_ids[x])
else:
router, network, subnet, cidr = vpn_utils.create_network(
self.neutron_client, self.neutron_admin_client,
self.suffixes[x])
self.rally_cidrs.append(cidr)
self.rally_subnets.append(subnet)
self.rally_networks.append(network)
self.rally_routers.append(router)
self.router_ids.append(router["router"]['id'])
self.rally_router_gw_ips.append(
router["router"]["external_gateway_info"]
["external_fixed_ips"][0]["ip_address"])
self.snat_namespaces.append(
vpn_utils.wait_for_namespace_creation(
"snat-", router, **kwargs))
self.qrouter_namespaces.append(
vpn_utils.wait_for_namespace_creation(
"qrouter-", router, **kwargs))
keypairs.append(vpn_utils.create_keypair(
self.nova_client, self.key_names[x],
self.key_file_paths[x]))
if self.qrouterns_compute_tuples:
vpn_utils.delete_hosts_from_knownhosts_file(
self.server_private_ips, self.qrouterns_compute_tuples,
self.private_key_file)
vpn_utils.delete_keyfiles(
self.local_key_files, self.remote_key_files,
self.qrouterns_compute_tuples, self.private_key_file)
else:
vpn_utils.delete_hosts_from_knownhosts_file(
self.server_private_ips)
vpn_utils.delete_keyfiles(self.local_key_files)
kwargs.update({
"nics":
[{"net-id": self.rally_networks[x]["network"]["id"]}],
"sec_group_suffix": self.suffixes[x],
"server_suffix": self.suffixes[x]
})
server = vpn_utils.create_nova_vm(
self.nova_client, keypairs[x], **kwargs)
self.server_private_ips.append(vpn_utils.get_server_ip(
self.nova_client, server.id, self.suffixes[x]))
self.servers.append(server)
def check_route(self):
LOG.debug("VERIFYING THAT THERE IS A ROUTE BETWEEN ROUTER "
"GATEWAYS")
for ns in self.snat_namespaces:
for ip in self.rally_router_gw_ips:
assert(True == vpn_utils.ping(ns, ip)), (
"PING FAILED FROM NAMESPACE " + ns + " TO IP "
+ ip)
def update_router(self, router_id, admin_state_up=False):
"""Updates router
:param router_id: router id
:param admin_state_up: update 'admin_state_up' of the router
:return:
"""
req_body = {'router': {'admin_state_up': admin_state_up}}
self.neutron_client.update_router(router_id, req_body)
def create_vpn_services(self):
with LOCK:
for x in range(MAX_RESOURCES):
self.vpn_services.append(self._create_vpn_service(
self.rally_subnets[x], self.rally_routers[x],
self.suffixes[x]))
def create_ipsec_site_connections(self, **kwargs):
with LOCK:
self.ipsec_site_connections = [
self._create_ipsec_site_connection(0, 1, **kwargs),
self._create_ipsec_site_connection(1, 0, **kwargs)
]
def assert_statuses(self, final_status, **kwargs):
LOG.debug("ASSERTING ACTIVE STATUSES FOR VPN-SERVICES AND "
"VPN-CONNECTIONS")
for x in range(MAX_RESOURCES):
self._assert_statuses(self.ipsec_site_connections[x],
self.vpn_services[x], final_status, **kwargs)
def assert_vpn_connectivity(self):
LOG.debug("VERIFY THE VPN CONNECTIVITY")
with LOCK:
assert(self._verify_vpn_connection(0, 1)), "VPN CONNECTION FAILED"
assert(self._verify_vpn_connection(1, 0)), "VPN CONNECTION FAILED"
self._delete_ipsec_site_connections()
self._delete_vpn_services()
self._delete_ipsec_policy()
self._delete_ike_policy()
vpn_utils.delete_networks(
self.neutron_client, self.neutron_admin_client, self.rally_routers,
self.rally_networks, self.rally_subnets)
if self.tenant_ids:
vpn_utils.delete_tenants(self.keystone_client, self.tenant_ids)

View File

@ -14,17 +14,11 @@
import exceptions
import os
from oslo_config import cfg
import paramiko
import socket
import stat
import time
def noop(*args, **kwargs):
pass
cfg.CONF.register_cli_opts = noop
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils as linux_utils
from rally.common import log as logging
from rally.plugins.openstack.wrappers import network as network_wrapper
from rally.task import utils as task_utils
@ -35,13 +29,71 @@ START_CIDR = "10.2.0.0/24"
EXT_NET_CIDR = "172.16.1.0/24"
def create_network(neutron_client, neutron_admin_client,
network_suffix, tenant_id=None):
"""Creates neutron network, subnet, router
def execute_cmd_over_ssh(host, cmd, private_key):
"""Run the given command over ssh
Using paramiko package, it creates a connection to the given host;
executes the required command on it and returns the output.
:param host: Dictionary of ip, username and password
:param cmd: Command to be run over ssh
:param private_key: path to private key file
:return: Output of the executed command
"""
LOG.debug('EXECUTE COMMAND <%s> OVER SSH', cmd)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
k = paramiko.RSAKey.from_private_key_file(private_key)
try:
client.connect(host["ip"], username=host["username"], pkey=k)
except paramiko.BadHostKeyException as e:
raise exceptions.Exception(
"BADHOSTKEY EXCEPTION WHEN CONNECTING TO %s", host["ip"], e)
except paramiko.AuthenticationException as e:
raise exceptions.Exception(
"AUTHENTICATION EXCEPTION WHEN CONNECTING TO %s",
host["ip"], e)
except paramiko.SSHException as e:
raise exceptions.Exception(
"SSH EXCEPTION WHEN CONNECTING TO %s", host["ip"], e)
except socket.error as e:
raise exceptions.Exception(
"SOCKET ERROR WHEN CONNECTING TO %s", host["ip"], e)
LOG.debug("CONNECTED TO HOST <%s>", host["ip"])
try:
stdin, stdout, stderr = client.exec_command(cmd)
return stdout.read().splitlines()
except paramiko.SSHException as e:
raise exceptions.Exception(
"SSHEXCEPTION WHEN CONNECTING TO %s", host["ip"], e)
finally:
client.close()
def create_tenant(keystone_client, tenant_suffix):
"""Creates keystone tenant with a random name.
:param keystone_client: keystone client
:param tenant_suffix: suffix name for the tenant
:returns: uuid of the new tenant
"""
tenant_name = "rally_tenant_" + tenant_suffix
LOG.debug("CREATING NEW TENANT %s", tenant_name)
return keystone_client.tenants.create(tenant_name).id
def create_network(neutron_client, neutron_admin_client, network_suffix,
tenant_id=None, DVR_flag=True, ext_net_name=None):
"""Create neutron network, subnet, router
:param neutron_client: neutron client
:param neutron_admin_client: neutron_admin_client
:param neutron_admin_client: neutron client with admin credentials
:param network_suffix: str, suffix name of the new network
:param tenant_id: uuid of the tenant
:param DVR_flag: True - creates a DVR router
False - creates a non DVR router
:param ext_net_name: external network that is to be used
:return: router, subnet, network, subnet_cidr
"""
subnet_cidr = network_wrapper.generate_cidr(start_cidr=START_CIDR)
@ -53,15 +105,13 @@ def create_network(neutron_client, neutron_admin_client,
network_args = {"name": network_name,
"router:external": is_external
}
LOG.debug("ADDING NEW NETWORK: %s", network_name)
if tenant_id is not None:
if tenant_id:
network_args["tenant_id"] = tenant_id
LOG.debug("ADDING NEW NETWORK %s", network_name)
return neutron_client.create_network({"network": network_args})
def _create_subnet(neutron_client, rally_network, network_suffix, cidr):
"""Creates neutron subnet"""
"""Create neutron subnet"""
network_id = rally_network["network"]["id"]
subnet_name = "rally_subnet_" + network_suffix
@ -70,19 +120,19 @@ def create_network(neutron_client, neutron_admin_client,
"network_id": network_id,
"ip_version": SUBNET_IP_VERSION
}
LOG.debug("ADDING SUBNET: %s", subnet_name)
if tenant_id is not None:
if tenant_id:
subnet_args["tenant_id"] = tenant_id
LOG.debug("ADDING SUBNET %s", subnet_name)
return neutron_client.create_subnet({"subnet": subnet_args})
def _create_router(neutron_client, ext_network_id, rally_subnet):
"""Creates router, sets the external gateway and adds router interface
def _create_router(neutron_client, ext_network_id, rally_subnet, dvr_flag):
"""Create router, set the external gateway and add router interface
:param neutron_client: neutron_client
:param ext_network_id: uuid of the external network
:param rally_subnet: subnet to add router interface
:param dvr_flag: True - creates a DVR router
False - creates a non DVR router
:return: router
"""
router_name = "rally_router_" + network_suffix
@ -90,81 +140,134 @@ def create_network(neutron_client, neutron_admin_client,
router_args = {"name": router_name,
"external_gateway_info": gw_info
}
LOG.debug("ADDING ROUTER: %s", router_name)
LOG.debug("ADDING ROUTER INTERFACE")
if tenant_id is not None:
if not dvr_flag:
router_args["distributed"] = dvr_flag
if tenant_id:
router_args["tenant_id"] = 'tenant_id'
LOG.debug("ADDING ROUTER %s", router_name)
rally_router = neutron_client.create_router({"router": router_args})
rally_router = neutron_client.create_router(
{"router": router_args})
LOG.debug("[%s]: ADDING ROUTER INTERFACE")
neutron_client.add_interface_router(
rally_router['router']["id"],
{"subnet_id": rally_subnet["subnet"]["id"]})
return rally_router
def _get_external_network_id():
"""Fetches the external network id, if external network exists"""
def _get_external_network_id(ext_net_name):
"""Fetch the network id for the given external network, if it exists.
Else fetch the first external network present.
"""
for network in neutron_client.list_networks()['networks']:
if network['router:external']:
ext_network_id = network['id']
LOG.debug("EXTERNAL NETWORK ALREADY EXISTS")
return ext_network_id
ext_nets = neutron_client.list_networks(
**{'router:external': True})['networks']
ext_nets_searched = [n for n in ext_nets if n['name'] == ext_net_name]
if ext_nets_searched:
return ext_nets_searched[0]['id']
elif ext_nets:
return ext_nets[0]['id']
else:
return None
def _create_external_network():
"""Creates external network and subnet"""
"""Creat external network and subnet"""
ext_net = _create_network(neutron_admin_client, "public", True)
_create_subnet(neutron_admin_client, ext_net, "public", EXT_NET_CIDR)
return ext_net['network']['id']
ext_network_id = _get_external_network_id()
ext_network_id = _get_external_network_id(ext_net_name)
if not ext_network_id:
ext_network_id = _create_external_network()
rally_network = _create_network(neutron_client, network_suffix)
rally_subnet = _create_subnet(neutron_client, rally_network,
network_suffix, subnet_cidr)
rally_router = _create_router(neutron_client, ext_network_id, rally_subnet)
rally_router = _create_router(neutron_client, ext_network_id,
rally_subnet, DVR_flag)
return rally_router, rally_network, rally_subnet, subnet_cidr
def create_tenant(keystone_client, tenant):
"""Creates keystone tenant with random name.
:param tenant: create a tenant with random name
:returns:
"""
return keystone_client.tenants.create(tenant)
def delete_tenant(keystone_client, tenant):
"""Deletes keystone tenant
:returns: delete keystone tenant instance
"""
if tenant:
for id in tenant:
keystone_client.tenants.delete(id)
def create_keypair(nova_client, key_name, key_file_path):
def create_keypair(nova_client, keypair_suffix):
"""Create keypair
:param nova_client: nova_client
:param key_name: key_name
:param key_file_path: path to key_file
:param keypair_suffix: sufix name for the keypair
:return: keypair
"""
LOG.debug("ADDING NEW KEYPAIR")
keypair = nova_client.keypairs.create(key_name)
f = open(key_file_path, 'w')
os.chmod(key_file_path, stat.S_IREAD | stat.S_IWRITE)
f.write(keypair.private_key)
f.close()
keypair_name = "rally_keypair_" + keypair_suffix
LOG.debug("CREATING A KEYPAIR %s", keypair_name)
keypair = nova_client.keypairs.create(keypair_name)
return keypair
def create_nova_vm(nova_client, keypair, **kwargs):
def write_key_to_local_path(keypair, local_key_file):
"""Write the private key of the nova instance to a temp file
:param keypair: nova keypair
:param local_key_file: path to private key file
:return:
"""
with open(local_key_file, 'w') as f:
os.chmod(local_key_file, stat.S_IREAD | stat.S_IWRITE)
f.write(keypair.private_key)
def write_key_to_compute_node(keypair, local_path, remote_path, host,
private_key):
"""Write the private key of the nova instance to the compute node
First fetches the private key from the keypair and writes it to a
temporary file in the local machine. It then sftp's the file
to the compute host.
:param keypair: nova keypair
:param local_path: path to private key file of the nova instance in the
local machine
:param remote_path: path where the private key file has to be placed
in the remote machine
:param host: compute host credentials
:param private_key: path to your private key file
:return:
"""
LOG.debug("WRITING PRIVATE KEY TO COMPUTE NODE")
k = paramiko.RSAKey.from_private_key_file(private_key)
write_key_to_local_path(keypair, local_path)
try:
transport = paramiko.Transport(host['ip'], host['port'])
except paramiko.SSHException as e:
raise exceptions.Exception(
"PARAMIKO TRANSPORT FAILED. CHECK IF THE HOST IP %s AND PORT %s "
"ARE CORRECT %s", host['ip'], host['port'], e)
try:
transport.connect(
username=host['username'], pkey=k)
except paramiko.BadHostKeyException as e:
transport.close()
raise exceptions.Exception(
"BADHOSTKEY EXCEPTION WHEN CONNECTING TO %s", host["ip"], e)
except paramiko.AuthenticationException as e:
transport.close()
raise exceptions.Exception(
"AUTHENTICATION EXCEPTION WHEN CONNECTING TO %s",
host["ip"], e)
except paramiko.SSHException as e:
transport.close()
raise exceptions.Exception(
"SSH EXCEPTION WHEN CONNECTING TO %s", host["ip"], e)
LOG.debug("CONNECTED TO HOST <%s>", host["ip"])
try:
sftp_client = paramiko.SFTPClient.from_transport(transport)
sftp_client.put(local_path, remote_path)
except IOError as e:
raise exceptions.Exception("FILE PATH DOESN'T EXIST", e)
finally:
transport.close()
def create_server(nova_client, keypair, **kwargs):
"""Create nova instance
:param nova_client: nova client
@ -172,10 +275,10 @@ def create_nova_vm(nova_client, keypair, **kwargs):
:return: new nova instance
"""
# add sec-group
sec_group_suffix = "rally_secgroup_" + kwargs["sec_group_suffix"]
LOG.debug("ADDING NEW SECURITY GROUP %s", sec_group_suffix)
secgroup = nova_client.security_groups.create(sec_group_suffix,
sec_group_suffix)
sec_group_name = "rally_secgroup_" + kwargs["sec_group_suffix"]
LOG.debug("ADDING NEW SECURITY GROUP %s", sec_group_name)
secgroup = nova_client.security_groups.create(sec_group_name,
sec_group_name)
# add security rules for SSH and ICMP
nova_client.security_group_rules.create(secgroup.id, from_port=22,
to_port=22, ip_protocol="tcp", cidr="0.0.0.0/0")
@ -186,14 +289,23 @@ def create_nova_vm(nova_client, keypair, **kwargs):
# boot new nova instance
server_name = "rally_server_" + (kwargs["server_suffix"])
LOG.debug("BOOTING NEW INSTANCE: %s", server_name)
LOG.debug("%s", kwargs["image"])
server = nova_client.servers.create(server_name,
image=kwargs["image"],
flavor=kwargs["flavor"],
key_name=keypair.name,
security_groups=[secgroup.id],
nics=kwargs["nics"])
return server
LOG.debug("WAITING FOR INSTANCE TO BECOME ACTIVE")
def assert_server_status(server, **kwargs):
"""Assert server status
:param server: nova server
"""
LOG.debug('WAITING FOR SERVER TO GO ACTIVE')
server = task_utils.wait_for(
server,
is_ready=task_utils.resource_is("ACTIVE"),
@ -201,10 +313,7 @@ def create_nova_vm(nova_client, keypair, **kwargs):
timeout=kwargs["nova_server_boot_timeout"],
check_interval=5)
LOG.debug("SERVER STATUS: %s", server.status)
assert('ACTIVE' == server.status), (
"THE INSTANCE IS NOT IN ACTIVE STATE")
return server
assert('ACTIVE' == server.status), ("THE INSTANCE IS NOT IN ACTIVE STATE")
def get_server_ip(nova_client, server_id, network_suffix):
@ -221,167 +330,237 @@ def get_server_ip(nova_client, server_id, network_suffix):
return server_ip
def get_namespace():
"""Get namespaces
def add_floating_ip(nova_client, server):
"""Associates floating-ip to a server
:param nova_client: nova client
:param server: nova instance
:return: associated floating ip
"""
fip_list = nova_client.floating_ips.list()
for fip in fip_list:
if fip.instance_id is None:
floating_ip = fip
break
else:
LOG.debug("CREATING NEW FLOATING IP")
floating_ip = nova_client.floating_ips.create()
LOG.debug("ASSOCIATING FLOATING IP %s", floating_ip.ip)
nova_client.servers.add_floating_ip(server.id, floating_ip.ip)
return floating_ip
def get_namespace(host, private_key):
"""SSH into the host and get the namespaces
:param host : dictionary of controller/compute node credentials
{ip:x.x.x.x, username:xxx, password:xxx}
:param private_key: path to private key file
:return: namespaces
"""
LOG.debug("GET NAMESPACES USING 'ip netns'")
cmd = ['ip', 'netns']
cmd = ip_lib.add_namespace_to_cmd(cmd)
try:
namespaces = linux_utils.execute(cmd)
except RuntimeError:
return None
LOG.debug("%s", namespaces)
LOG.debug("GET NAMESPACES")
cmd = "sudo ip netns"
namespaces = execute_cmd_over_ssh(host, cmd, private_key)
LOG.debug("NAMESPACES %s", namespaces)
return namespaces
def wait_for_namespace_creation(namespace, rally_router, **kwargs):
"""Wait for namespace creation
def wait_for_namespace_creation(namespace_tag, router_id, hosts, private_key,
timeout=60):
"""Wait for the namespace creation
:param namespace: snat/qrouter namespace
:param rally_router: rally_router
Get into each of the controllers/compute nodes and check which one contains
the snat/qrouter namespace corresponding to rally_router. Sleep for a sec
and repeat until either the namespace is found or the namespace_creation_
time exceeded.
:param namespace_tag: which namespace ("snat_" or "qrouter_")
:param router_id: uuid of the rally_router
:param hosts: controllers or compute hosts
:param private_key: path to private key file
:param timeout: namespace creation time
:return:
"""
start_time = time.time()
while True:
namespaces = get_namespace().split()
for line in namespaces:
if line == (namespace + rally_router["router"]["id"]):
namespace = line
return namespace
for host in hosts:
namespaces = get_namespace(host, private_key)
for line in namespaces:
if line == (namespace_tag + router_id):
namespace_tag = line
return namespace_tag, host
time.sleep(1)
if time.time() - start_time > kwargs['namespace_creation_timeout']:
raise exceptions.Exception("Timeout while waiting for"
" namespaces to be created")
if time.time() - start_time > timeout:
raise exceptions.Exception("TIMEOUT WHILE WAITING FOR"
" NAMESPACES TO BE CREATED")
def ping(namespace, ip):
"""Pings ip address from network namespace.
In order to ping it uses following cli command:
ip netns exec <namespace> ping -c 4 -q <ip>
:param namespace: namespace
:param ip: ip to ping to
"""
LOG.debug("PING %s FROM THE NAMESPACE %s", ip, namespace)
count = 4
cmd = ['ping', '-w', 2 * count, '-c', count, ip]
cmd = ip_lib.add_namespace_to_cmd(cmd, namespace)
try:
ping_result = linux_utils.execute(cmd, run_as_root=True)
except RuntimeError:
def ping(host, cmd, private_key):
"""Execute ping command over ssh"""
ping_result = execute_cmd_over_ssh(host, cmd, private_key)
if ping_result:
LOG.debug("PING RESULT %s", ping_result)
return True
else:
return False
LOG.debug("%s", ping_result)
return True
def get_interfaces(namespace):
"""Do an "ip a".
def ping_router_gateway(namespace_controller_tuple, router_gw_ip, private_key):
"""Ping the ip address from network namespace
In order to do "ip a" it uses following cli command:
ip netns exec <namespace> ip a | grep qg
:param namespace: namespace
Get into controller's snat-namespaces and ping the peer router gateway ip.
:param namespace_controller_tuple: namespace, controller tuple. (It's the
controller that contains the namespace )
:param router_gw_ip: ip address to be pinged
:param private_key: path to private key file
:return: True if ping succeeds
False if ping fails
"""
namespace, controller = namespace_controller_tuple
LOG.debug("PING %s FROM THE NAMESPACE %s", router_gw_ip, namespace)
count = 4
cmd = "sudo ip netns exec {} ping -w {} -c {} {}".format(
namespace, 2 * count, count, router_gw_ip)
return ping(controller, cmd, private_key)
def get_interfaces(namespace_controller_tuple, private_key):
"""Get the interfaces
Get into the controller's snat namespace and list the interfaces.
:param namespace_controller_tuple: namespace, controller tuple(the
controller that contains the namespace).
:param private_key: path to private key file
:return: interfaces
"""
namespace, controller = namespace_controller_tuple
LOG.debug("GET THE INTERFACES BY USING 'ip a' FROM THE NAMESPACE %s",
namespace)
cmd = ['ip', 'a']
cmd = ip_lib.add_namespace_to_cmd(cmd, namespace)
try:
interfaces = linux_utils.execute(cmd, run_as_root=True)
except RuntimeError:
return None
LOG.debug("%s", interfaces)
cmd = "sudo ip netns exec {} ip a".format(namespace)
interfaces = execute_cmd_over_ssh(controller, cmd, private_key)
LOG.debug("INTERFACES %s", interfaces)
return interfaces
def start_tcpdump(namespace, interface):
"""Starts tcpdump at the given interface
def start_tcpdump(namespace_controller_tuple, interface, private_key):
"""Start the tcpdump at the given interface
In order to start a "tcpdump" it uses the following command:
ip netns exec <namespace> sudo tcpdump -i <interface>
:param namespace: namespace
:param interface: interface
:return: tcpdump
Get into the controller's snat namespace and start a tcp dump at the
qg-interface.
:param namespace_controller_tuple: namespace, controller tuple. (It's the
controller that contains the namespace )
:param interface: interface in which tcpdump has to be run
:param private_key: path to private key file
:return: tcpdump output
"""
LOG.debug("START THE TCPDUMP USING 'tcpdump -i <%s> FROM THE NAMESPACE"
namespace, controller = namespace_controller_tuple
LOG.debug("START THE TCPDUMP USING 'tcpdump -i %s FROM THE NAMESPACE"
" %s", interface, namespace)
cmd = ['timeout', '5', 'tcpdump', '-n', '-i', interface]
cmd = ip_lib.add_namespace_to_cmd(cmd, namespace)
try:
tcpdump = linux_utils.execute(cmd, run_as_root=True,
extra_ok_codes=[124])
except RuntimeError:
return None
LOG.debug("%s", tcpdump)
cmd = ("sudo ip netns exec {} timeout 15 tcpdump -n -i {}"
.format(namespace, interface))
tcpdump = execute_cmd_over_ssh(controller, cmd, private_key)
LOG.debug("TCPDUMP %s", tcpdump)
return tcpdump
def ssh_and_ping_server(ssh_server, ping_server, namespace, key_file_name):
"""SSH into the server from the namespace.
def ssh_and_ping_server(local_server, peer_server, ns_compute_tuple, keyfile,
private_key):
"""SSH and ping the nova instance from the namespace
In order to ssh it uses the following command:
ip netns exec <namespace> ssh -i <path to keyfile> cirros@<server_ip>
:param ssh_server: ip of the server to ssh into
:param ping_server: ip of the server to ping to
:param namespace: qrouter namespace
:param key_file_name: path to private key file
:return: True/False
Get into the compute node's qrouter namespace and then ssh into the local
nova instance & ping the peer nova instance.
:param local_server: private ip of the server to ssh into
:param peer_server: private ip of the server to ping to
:param ns_compute_tuple: namespace, compute tuple. (It's the
compute node that contains the namespace )
:param keyfile: path to private key file of the nova instance
:param private_key: path to private key file
:return: True if ping succeeds
False if ping fails
"""
namespace, compute_host = ns_compute_tuple
LOG.debug("SSH INTO SERVER %s AND PING THE PEER SERVER %s FROM THE"
" NAMESPACE %s", ssh_server, ping_server, namespace)
host = "cirros@" + ssh_server
" NAMESPACE %s", local_server, peer_server, namespace)
host = "cirros@" + local_server
count = 20
cmd = ['ssh', '-o', 'StrictHostKeyChecking=no', '-o', 'HashKnownHosts=no',
'-i', key_file_name, host, 'ping', '-w', 2 * count, '-c', count,
ping_server]
cmd = ip_lib.add_namespace_to_cmd(cmd, namespace)
try:
ping_result = linux_utils.execute(cmd, run_as_root=True)
except RuntimeError:
return False
LOG.debug("%s", ping_result)
return True
cmd = ("sudo ip netns exec {} ssh -v -o StrictHostKeyChecking=no -o"
"HashKnownHosts=no -i {} {} ping -w {} -c {} {}"
.format(namespace, keyfile, host, 2 * count, count, peer_server))
return ping(compute_host, cmd, private_key)
def ssh_and_ping_server_with_fip(local_server, peer_server, keyfile,
private_key):
"""SSH into the local nova instance and ping the peer instance using fips
:param local_server: fip of the server to ssh into
:param peer_server: private ip of the server to ping to
:param keyfile: path to private key file of the nova instance
:param private_key: path to private key file
:return: True if ping succeeds
False if ping fails
"""
LOG.debug("SSH INTO LOCAL SERVER %s AND PING THE PEER SERVER %s",
local_server.ip, peer_server)
count = 20
local_host = {"ip": "127.0.0.1", "username": None}
host = "cirros@" + local_server.ip
cmd = ("ssh -v -o StrictHostKeyChecking=no -o"
"HashKnownHosts=no -i {} {} ping -w {} -c {} {}"
.format(keyfile, host, 2 * count, count, peer_server))
return ping(local_host, cmd, private_key)
def delete_servers(nova_client, servers):
"""Delete nova servers
It deletes the nova servers, associated security groups and keypairs.
It deletes the nova servers, associated security groups.
:param nova_client: nova client
:param servers: nova instances to be deleted
:return:
"""
if servers:
for server in servers:
if "rally" in server.name:
sec_group_name = server.security_groups[0]['name']
server_key_name = server.key_name
for server in servers:
LOG.debug("DELETING NOVA INSTANCE: %s", server.id)
sec_group_id = server.security_groups[0]['name']
nova_client.servers.delete(server.id)
LOG.debug("DELETING NOVA INSTANCE: %s", server.id)
nova_client.servers.delete(server.id)
LOG.debug("WAITING FOR INSTANCE TO GET DELETED")
task_utils.wait_for_delete(
server, update_resource=task_utils.get_from_manager())
LOG.debug("WAITING FOR INSTANCE TO GET DELETED")
task_utils.wait_for_delete(
server, update_resource=task_utils.get_from_manager())
for secgroup in nova_client.security_groups.list():
if secgroup.name == sec_group_name:
LOG.debug("DELETING SEC_GROUP: %s", sec_group_name)
nova_client.security_groups.delete(secgroup.id)
for key_pair in nova_client.keypairs.list():
if key_pair.name == server_key_name:
LOG.debug("DELETING KEY_PAIR: %s", server_key_name)
nova_client.keypairs.delete(key_pair.id)
for secgroup in nova_client.security_groups.list():
if secgroup.id == sec_group_id:
LOG.debug("DELETING SEC_GROUP: %s", sec_group_id)
nova_client.security_groups.delete(secgroup.id)
def delete_network(neutron_client, neutron_admin_client,
def delete_floating_ips(nova_client, fips):
"""Delete floating ips
:param nova_client: nova client
:param fips: list of floating ips
:return:
"""
for fip in fips:
nova_client.floating_ips.delete(fip.id)
def delete_keypairs(nova_client, keypairs):
"""Delete key pairs
:param nova_client: nova client
:param keypairs: list of keypairs
:return
"""
for key_pair in keypairs:
LOG.debug("DELETING KEY_PAIR %s", key_pair.name)
nova_client.keypairs.delete(key_pair.id)
def delete_networks(neutron_client, neutron_admin_client,
routers, networks, subnets):
"""Delete neutron network, subnets amd routers.
"""Delete neutron network, subnets amd routers
:param neutron_client: neutron client
:param neutron_admin_client: neutron_admin_client
@ -391,64 +570,87 @@ def delete_network(neutron_client, neutron_admin_client,
:return
"""
LOG.debug("DELETING RALLY ROUTER INTERFACES & GATEWAYS")
if routers:
for router in routers:
if "rally" in router['router']['name']:
neutron_client.remove_gateway_router(router['router']['id'])
router_name = router['router']['name']
subnet_name = ("rally_subnet_" +
router_name[13:len(router_name)])
if subnets:
for subnet in subnets:
if subnet_name == subnet['subnet']['name']:
neutron_client.remove_interface_router(
router['router']['id'],
{"subnet_id": subnet['subnet']['id']})
for router in routers:
neutron_client.remove_gateway_router(router['router']['id'])
router_name = router['router']['name']
subnet_name = ("rally_subnet_" + router_name[13:len(router_name)])
for subnet in subnets:
if subnet_name == subnet['subnet']['name']:
neutron_client.remove_interface_router(
router['router']['id'],
{"subnet_id": subnet['subnet']['id']})
LOG.debug("DELETING RALLY ROUTERS")
if routers:
for router in routers:
if "rally" in router['router']['name']:
neutron_client.delete_router(router['router']['id'])
for router in routers:
neutron_client.delete_router(router['router']['id'])
LOG.debug("DELETING RALLY NETWORKS")
if networks:
for network in networks:
if (network['network']['router:external'] and
network['network']['name'] == "rally_network_public"):
external_network = network
neutron_admin_client.delete_network(
external_network['network']["id"])
if "rally_network" in network['network']['name']:
neutron_client.delete_network(network['network']['id'])
for network in networks:
if (network['network']['router:external'] and
network['network']['name'] == "rally_network_public"):
external_network = network
neutron_admin_client.delete_network(
external_network['network']["id"])
elif network['network']['router:external']:
pass
else:
neutron_client.delete_network(network['network']['id'])
def delete_key_files(key_file_paths):
"""Deletes ssh key files
def delete_tenants(keystone_client, tenant_ids):
"""Delete keystone tenant
:param key_file_paths: paths to ssh key files
:param keystone_client: keystone client
:param tenant_ids: list of tenants' uuids
:returns: delete keystone tenant instance
"""
LOG.debug('DELETE TENANTS')
for id in tenant_ids:
keystone_client.tenants.delete(id)
def delete_keyfiles(local_key_files, remote_key_files=None,
ns_compute_tuples=None, private_key=None):
"""Delete the SSH keyfiles from the compute and the local nodes
:param local_key_files: paths to ssh key files in local node
:param remote_key_files: paths to ssh key files in compute nodes
:param ns_compute_tuples: namespace, compute tuple. (It's the
compute node that contains the namespace )
:param private_key: path to private key file
:return:
"""
LOG.debug("DELETING RALLY KEY FILES")
if key_file_paths:
for path in key_file_paths:
if os.path.exists(path):
os.remove(path)
LOG.debug("DELETING RALLY KEY FILES FROM LOCAL MACHINE")
for key in local_key_files:
if os.path.exists(key):
os.remove(key)
if ns_compute_tuples:
LOG.debug("DELETING RALLY KEY FILES FROM COMPUTE HOSTS")
for key, ns_comp in zip(remote_key_files, ns_compute_tuples):
cmd = "sudo rm -f {}".format(key)
host = ns_comp[1]
execute_cmd_over_ssh(host, cmd, private_key)
def delete_hosts_from_knownhosts_file(hosts):
"""Removes the hosts from the knownhosts file
def delete_hosts_from_knownhosts_file(hosts, ns_compute_tuples=None,
private_key=None):
"""Remove the hosts from the knownhosts file
:param hosts: host ips to be removed from /root/.ssh/knownhosts
:param ns_compute_tuples: namespace, compute tuple. (It's the
compute node that contains the namespace )
:param private_key: path to private key file
:return:
"""
LOG.debug("DELETES HOSTS FROM THE KNOWNHOSTS FILE")
if hosts:
if ns_compute_tuples:
LOG.debug("DELETES HOSTS FROM THE KNOWNHOSTS FILE")
for host, ns_comp in zip(hosts, ns_compute_tuples):
compute_host = ns_comp[1]
cmd = ("sudo ssh-keygen -f /root/.ssh/known_hosts -R"
" {}".format(host))
execute_cmd_over_ssh(compute_host, cmd, private_key)
else:
for host in hosts:
cmd = ['ssh-keygen', '-f', "/root/.ssh/known_hosts", '-R', host]
cmd = ip_lib.add_namespace_to_cmd(cmd)
try:
linux_utils.execute(cmd, run_as_root=True)
except RuntimeError:
return False
return True
os.system("sudo ssh-keygen -f /root/.ssh/known_hosts -R"
" {}".format(host))

View File

@ -0,0 +1,17 @@
{
"image_name": "^cirros.*-disk$",
"private_key": "<path-to-private-key-file>",
"controller_creds": [
{
"ip": "x.x.x.x",
"username": "xxx"
}
],
"compute_creds": [
{
"ip": "x.x.x.x",
"username": "xxx",
"port": 22
}
]
}

View File

@ -1,6 +1,6 @@
{% set image_name = image_name or "cirros-0.3.3-x86_64-disk" %}
---
VpnBasicScenario.create_and_delete_vpn_connection:
TestVpnBasicScenario.create_and_delete_vpn_connection:
-
args:
flavor:
@ -19,8 +19,14 @@
secret: "secret"
nova_server_boot_timeout: 60 * 6
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 400
ipsec_site_connection_creation_timeout: 180
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: True
use_admin_client: False
ext-net: "ext-net"
runner:
type: "constant"
@ -35,7 +41,7 @@
failure_rate:
max: 0
TestVPNStatusScenario.check_vpn_status:
TestVpnStatusScenario.check_vpn_status:
-
args:
flavor:
@ -46,6 +52,12 @@
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 400
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: True
use_admin_client: False
ext-net: "ext-net"
runner:
type: "constant"
@ -60,7 +72,7 @@
failure_rate:
max: 0
VpnTenantScenario.multitenants_vpn_test:
TestVpnTenantScenario.multitenants_vpn_test:
-
args:
flavor:
@ -69,8 +81,14 @@
name: {{image_name}}
nova_server_boot_timeout: 60 * 6
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 400
ipsec_site_connection_creation_timeout: 180
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: True
use_admin_client: True
ext-net: "ext-net"
runner:
type: "constant"

View File

@ -0,0 +1,103 @@
---
TestVpnBasicScenario.create_and_delete_vpn_connection:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
phase1_negotiation_mode: "main"
auth_algorithm: "sha1"
encryption_algorithm: "aes-128"
pfs: "group5"
value: 7200
ike_version: "v1"
transform_protocol: "esp"
encapsulation_mode: "tunnel"
mtu: 1500
secret: "secret"
nova_server_boot_timeout: 60 * 6
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 180
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: False
use_admin_client: True
ext-net: "ext-net"
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
sla:
failure_rate:
max: 0
TestVpnStatusScenario.check_vpn_status:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
nova_server_boot_timeout: 60 * 6
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 400
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: False
use_admin_client: True
ext-net: "ext-net"
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
sla:
failure_rate:
max: 0
TestVpnTenantScenario.multitenants_vpn_test:
-
args:
flavor:
name: "m1.tiny"
image:
name: {{image_name}}
nova_server_boot_timeout: 60 * 6
vpn_service_creation_timeout: 100
ipsec_site_connection_creation_timeout: 180
namespace_creation_timeout: 60
private_key: {{private_key}}
controller_creds: {{controller_creds}}
compute_creds: {{compute_creds}}
DVR_flag: False
use_admin_client: True
ext-net: "ext-net"
runner:
type: "constant"
times: 1
concurrency: 1
context:
users:
tenants: 1
users_per_tenant: 1
sla:
failure_rate:
max: 0