diff --git a/plugin_test/helpers/contrail_client.py b/plugin_test/helpers/contrail_client.py new file mode 100644 index 000000000..e3c0751bd --- /dev/null +++ b/plugin_test/helpers/contrail_client.py @@ -0,0 +1,125 @@ +"""Copyright 2016 Mirantis, Inc. + +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 +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. +""" +from fuelweb_test import logwrap +from fuelweb_test import logger +from fuelweb_test.helpers.decorators import json_parse +from fuelweb_test.helpers.http import HTTPClient +from fuelweb_test.settings import KEYSTONE_CREDS + + +class ContrailClient(object): + """Contrail utilite wrapper.""" + + def __init__(self, controller_node_ip, **kwargs): + """Create ContrailClient object.""" + url = "http://{0}:8082".format(controller_node_ip) + logger.info('Initiate Nailgun client with url %s', url) + self.keystone_url = "http://{0}:5000/v2.0".format(controller_node_ip) + self._client = HTTPClient(url=url, keystone_url=self.keystone_url, + credentials=KEYSTONE_CREDS, + **kwargs) + super(ContrailClient, self).__init__() + + @property + def client(self): + """Client property.""" + return self._client + + @logwrap + @json_parse + def create_network(self, net_name, net_attr): + """Create virtual-network. + + :param project_name: type list, tenant(project), network name + :param net_attr: type dictionary, network attributes + """ + data = { + "virtual-network": { + "parent_type": "project", + "fq_name": net_name, + "network_ipam_refs": net_attr}} + return self.client.post( + '/virtual-networks', data=data) + + @json_parse + def add_router_interface(self, network, route_table, attr=None): + """Add router interface to network. + + :param network: type dictionary, network + :param route_table: type dictionary, router + :param attr: type dictionary, parameters of router interface(optianal) + """ + data = {"virtual-network": {'fq_name': network['fq_name'], + 'route_table_refs': [{ + 'to': route_table['fq_name'], "attr":attr}]}} + return self.client.put( + '/virtual-network/{0}'.format(network['uuid']), data=data) + + @json_parse + def get_route_tables(self): + """Get router.""" + return self.client.get('/route-tables') + + @json_parse + def get_networks(self): + """Get networks.""" + return self.client.get('/virtual-networks') + + def get_router_by_name(self, name): + """Get router by name. + + :param name: type string, name of router. + :return dictionary + """ + route_tables = self.get_route_tables()['route-tables'] + route_table = [ + route for route in route_tables + if name in route['fq_name']] + return route_table.pop() + + @json_parse + def get_projects(self): + """Get router.""" + return self.client.get('/projects') + + def get_project_by_name(self, name): + """Get project by name. + + :param name: type string, name of project. + :return dictionary + """ + projects = self.get_projects() + project = [p for p in projects if name in p['fq_name']] + return project.pop() + + @json_parse + def get_instance_by_id(self, instance_id): + """Get instance by id. + + :param instance_id: type string, instance id. + :return dictionary + """ + return self.client.get( + '/virtual-machine/{0}'.format(instance_id)) + + @json_parse + def get_net_by_id(self, net_id): + """Get network by id. + + :param net_id: type string, instance id. + :return dictionary + """ + return self.client.get( + '/virtual-network/{0}'.format(net_id)) diff --git a/plugin_test/run_tests.py b/plugin_test/run_tests.py index 8b818eb47..20d1e46fa 100644 --- a/plugin_test/run_tests.py +++ b/plugin_test/run_tests.py @@ -46,6 +46,7 @@ def import_tests(): from tests import test_functional from tests import test_network_templates from tests import test_failover + from tests import test_system def run_tests(): diff --git a/plugin_test/tests/test_system.py b/plugin_test/tests/test_system.py new file mode 100644 index 000000000..17a688275 --- /dev/null +++ b/plugin_test/tests/test_system.py @@ -0,0 +1,270 @@ +"""Copyright 2016 Mirantis, Inc. + +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 +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 os + +from proboscis import test +from devops.helpers.helpers import tcp_ping +from devops.helpers.helpers import wait +from fuelweb_test import logger +from fuelweb_test.helpers import os_actions +from fuelweb_test.settings import CONTRAIL_PLUGIN_PACK_UB_PATH +from fuelweb_test.helpers.decorators import log_snapshot_after_test +from fuelweb_test.tests.base_test_case import SetupEnvironment +from fuelweb_test.tests.base_test_case import TestBasic +from helpers.contrail_client import ContrailClient +from fuelweb_test.settings import SERVTEST_PASSWORD +from fuelweb_test.settings import SERVTEST_TENANT +from fuelweb_test.settings import SERVTEST_USERNAME +from proboscis.asserts import assert_true +from helpers import plugin +from helpers import openstack + + +@test(groups=["plugins"]) +class SystemTests(TestBasic): + """System test suite. + + The goal of integration and system testing is to ensure that new or + modified components of Fuel and MOS work effectively with + Contrail API without gaps in dataflow. + """ + + pack_copy_path = '/var/www/nailgun/plugins/contrail-4.0' + add_package = \ + '/var/www/nailgun/plugins/contrail-4.0/' \ + 'repositories/ubuntu/contrail-setup*' + ostf_msg = 'OSTF tests passed successfully.' + cluster_id = '' + pack_path = CONTRAIL_PLUGIN_PACK_UB_PATH + CONTRAIL_DISTRIBUTION = os.environ.get('CONTRAIL_DISTRIBUTION') + + def ping_instance_from_instance(self, os_conn, node_name, + ip_pair, ping_result=0): + """Check network connectivity between instances by ping. + + :param os_conn: type object, openstack + :param ip_pair: type list, pair floating ips of instances + :param ping_result: type interger, exite code of command execution + by default is 0 + """ + creds = ("cirros", "cubswin:)") + for ip_from in ip_pair: + with self.fuel_web.get_ssh_for_node(node_name) as remote: + for ip_to in ip_pair[ip_from]: + command = "ping -c 5 {0}".format(ip_to) + logger.info( + "Check connectin from {0} to {1}.".format( + ip_from, ip_to)) + assert_true( + os_conn.execute_through_host( + remote, ip_from, command, + creds)['exit_code'] == ping_result, + 'Ping responce is not received.') + + @test(depends_on=[SetupEnvironment.prepare_slaves_5], + groups=["systest_setup"]) + @log_snapshot_after_test + def systest_setup(self): + """Setup for system test suite. + + Scenario: + 1. Create an environment with "Neutron with tunneling + segmentation" as a network configuration + 2. Enable Contrail plugin + 3. Add 1 node with contrail-config, contrail-control and + contrail-db roles + 4. Add a node with controller role + 5. Add a node with compute role + 6. Deploy cluster with plugin + 7. Run OSTF. + + Duration 90 min + + """ + self.show_step(1) + plugin.prepare_contrail_plugin(self, slaves=5) + + # activate vSRX image + plugin.activate_vsrx() + + self.show_step(2) + plugin.activate_plugin(self) + + plugin.show_range(self, 3, 6) + self.fuel_web.update_nodes( + self.cluster_id, + { + 'slave-01': ['contrail-config', + 'contrail-control', + 'contrail-db'], + 'slave-02': ['controller'], + 'slave-03': ['compute'], + 'slave-04': ['compute'], + }) + + self.show_step(6) + openstack.deploy_cluster(self) + self.show_step(7) + self.fuel_web.run_ostf( + cluster_id=self.cluster_id, test_sets=['smoke']) + + @test(depends_on=[systest_setup], + groups=["create_new_network_via_contrail"]) + @log_snapshot_after_test + def create_new_network_via_contrail(self): + """Create a new network via Contrail. + + Scenario: + 1. Setup systest_setup. + 2. Create a new network via Contrail API. + 3. Launch 2 new instance in the network with default security group + via Horizon API. + 4. Check ping connectivity between instances. + 5. Verify on Contrail controller WebUI that network is there and + VMs are attached to it. + + Duration: 15 min + + """ + # constants + net_name = 'net_1' + self.show_step(1) + cluster_id = self.fuel_web.get_last_created_cluster() + + self.show_step(2) + os_ip = self.fuel_web.get_public_vip(cluster_id) + contrail_client = ContrailClient(os_ip) + network = contrail_client.create_network([ + "default-domain", SERVTEST_TENANT, net_name], + [{"attr": { + "ipam_subnets": [{ + "subnet": { + "ip_prefix": '10.1.1.0', + "ip_prefix_len": 24}}]}, + "to": ["default-domain", + "default-project", + "default-network-ipam"]}])['virtual-network'] + default_router = contrail_client.get_router_by_name(SERVTEST_TENANT) + contrail_client.add_router_interface(network, default_router) + + self.show_step(3) + os_conn = os_actions.OpenStackActions( + os_ip, SERVTEST_USERNAME, + SERVTEST_PASSWORD, + SERVTEST_TENANT) + + hypervisors = os_conn.get_hypervisors() + instances = [] + fips = [] + for hypervisor in hypervisors: + instance = os_conn.create_server_for_migration( + neutron=True, + availability_zone="nova:{0}".format( + hypervisor.hypervisor_hostname), + label=net_name + ) + instances.append(instance) + ip = os_conn.assign_floating_ip(instance).ip + wait( + lambda: tcp_ping(ip, 22), timeout=60, interval=5, + timeout_msg="Node {0} is not accessible by SSH.".format(ip)) + fips.append(ip) + + self.show_step(4) + ip_pair = dict.fromkeys(fips) + for key in ip_pair: + ip_pair[key] = [value for value in fips if key != value] + controller = self.fuel_web.get_nailgun_primary_node( + self.env.d_env.nodes().slaves[1]) + self.ping_instance_from_instance(os_conn, controller.name, ip_pair) + + self.show_step(5) + net_info = contrail_client.get_net_by_id( + network['uuid'])['virtual-network'] + net_interface_ids = [] + for interface in net_info['virtual_machine_interface_back_refs']: + net_interface_ids.append(interface['uuid']) + for instance in instances: + interf_id = contrail_client.get_instance_by_id( + instance.id)['virtual-machine'][ + 'virtual_machine_interface_back_refs'][0]['uuid'] + assert_true( + interf_id in net_interface_ids, + '{0} is not attached to network {1}'.format( + instance.name, net_name)) + + @test(depends_on=[systest_setup], groups=["create_networks"]) + @log_snapshot_after_test + def create_networks(self): + """Create and terminate networks and verify in Contrail UI. + + Scenario: + 1. Setup systest_setup. + 2. Add 2 private networks via Horizon. + 3. Verify that networks are present in Contrail UI. + 4. Remove one of the private network via Horizon. + 5. Verify that the network is absent in Contrail UI. + 6. Add a private network via Horizon. + 7. Verify that all networks are present in Contrail UI. + + Duration: 15 min + + """ + # constants + net_names = ['net_1', 'net_2'] + + self.show_step(1) + cluster_id = self.fuel_web.get_last_created_cluster() + + self.show_step(2) + os_ip = self.fuel_web.get_public_vip(cluster_id) + contrail_client = ContrailClient(os_ip) + os_conn = os_actions.OpenStackActions( + os_ip, SERVTEST_USERNAME, + SERVTEST_PASSWORD, + SERVTEST_TENANT) + networks = [] + for net in net_names: + logger.info('Create network {}'.format(net)) + network = os_conn.create_network( + network_name=net)['network'] + networks.append(network) + + self.show_step(3) + for net in networks: + net_contrail = contrail_client.get_net_by_id( + net['id'])['virtual-network']['uuid'] + assert_true(net['id'] == net_contrail) + + self.show_step(4) + remove_net_id = networks.pop()['id'] + os_conn.neutron.delete_network(remove_net_id) + + self.show_step(5) + contrail_networks = contrail_client.get_networks()['virtual-networks'] + assert_true( + remove_net_id not in [net['uuid'] for net in contrail_networks]) + + self.show_step(6) + network = os_conn.create_network( + network_name=net_names.pop())['network'] + networks.append(network) + + self.show_step(7) + for net in networks: + net_contrail = contrail_client.get_net_by_id( + net['id'])['virtual-network']['uuid'] + assert_true(net['id'] == net_contrail)