From f0ceb33f3a31fc5a3c8ed18971cc19009a10259f Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Thu, 18 Jun 2020 15:03:17 +0200 Subject: [PATCH] Add functional tests for neutron-arista --- zaza/openstack/charm_tests/neutron/tests.py | 21 +++++- .../charm_tests/neutron_arista/__init__.py | 15 +++++ .../charm_tests/neutron_arista/setup.py | 39 +++++++++++ .../charm_tests/neutron_arista/tests.py | 53 +++++++++++++++ .../charm_tests/neutron_arista/utils.py | 67 +++++++++++++++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 zaza/openstack/charm_tests/neutron_arista/__init__.py create mode 100644 zaza/openstack/charm_tests/neutron_arista/setup.py create mode 100644 zaza/openstack/charm_tests/neutron_arista/tests.py create mode 100644 zaza/openstack/charm_tests/neutron_arista/utils.py diff --git a/zaza/openstack/charm_tests/neutron/tests.py b/zaza/openstack/charm_tests/neutron/tests.py index 7e28112..ba014e2 100644 --- a/zaza/openstack/charm_tests/neutron/tests.py +++ b/zaza/openstack/charm_tests/neutron/tests.py @@ -293,8 +293,23 @@ class NeutronCreateNetworkTest(test_utils.OpenStackBaseTest): cls.neutron_client = ( openstack_utils.get_neutron_session_client(cls.keystone_session)) + def _test_400_additional_validation(self, expected_network_names): + """Additional assertions for test_400_create_network. + + Can be overridden in derived classes. + + :type expected_network_names: List[str] + """ + pass + def test_400_create_network(self): - """Create a network, verify that it exists, and then delete it.""" + """Create a network, verify that it exists, and then delete it. + + Additional verifications on the created network can be performed by + deriving this class and overriding _test_400_additional_validation(). + """ + self._test_400_additional_validation([]) + logging.debug('Creating neutron network...') self.neutron_client.format = 'json' net_name = 'test_net' @@ -319,10 +334,14 @@ class NeutronCreateNetworkTest(test_utils.OpenStackBaseTest): network = networks['networks'][0] assert network['name'] == net_name, "network ext_net not found" + self._test_400_additional_validation([net_name]) + # Cleanup logging.debug('Deleting neutron network...') self.neutron_client.delete_network(network['id']) + self._test_400_additional_validation([]) + class NeutronApiTest(NeutronCreateNetworkTest): """Test basic Neutron API Charm functionality.""" diff --git a/zaza/openstack/charm_tests/neutron_arista/__init__.py b/zaza/openstack/charm_tests/neutron_arista/__init__.py new file mode 100644 index 0000000..8d3f5c9 --- /dev/null +++ b/zaza/openstack/charm_tests/neutron_arista/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Canonical Ltd. +# +# 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. + +"""Collection of code for setting up and testing neutron-arista.""" diff --git a/zaza/openstack/charm_tests/neutron_arista/setup.py b/zaza/openstack/charm_tests/neutron_arista/setup.py new file mode 100644 index 0000000..50878e6 --- /dev/null +++ b/zaza/openstack/charm_tests/neutron_arista/setup.py @@ -0,0 +1,39 @@ +# Copyright 2020 Canonical Ltd. +# +# 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. + +"""Code for setting up neutron-arista.""" + +import logging +import tenacity +import zaza +import zaza.openstack.charm_tests.neutron_arista.utils as arista_utils + + +def test_fixture(): + """Pass arista-virt-test-fixture's IP address to neutron-arista.""" + fixture_ip_addr = arista_utils.fixture_ip_addr() + logging.info( + "{}'s IP address is '{}'. Passing it to neutron-arista...".format( + arista_utils.FIXTURE_APP_NAME, fixture_ip_addr)) + zaza.model.set_application_config('neutron-arista', + {'eapi-host': fixture_ip_addr}) + + logging.info('Waiting for {} to become ready...'.format( + arista_utils.FIXTURE_APP_NAME)) + for attempt in tenacity.Retrying( + wait=tenacity.wait_fixed(10), # seconds + stop=tenacity.stop_after_attempt(30), + reraise=True): + with attempt: + arista_utils.query_fixture_networks(fixture_ip_addr) diff --git a/zaza/openstack/charm_tests/neutron_arista/tests.py b/zaza/openstack/charm_tests/neutron_arista/tests.py new file mode 100644 index 0000000..c47c9c8 --- /dev/null +++ b/zaza/openstack/charm_tests/neutron_arista/tests.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Canonical Ltd. +# +# 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. + +"""Encapsulating `neutron-arista` testing.""" + +import logging +import tenacity +import zaza.openstack.charm_tests.neutron.tests as neutron_tests +import zaza.openstack.charm_tests.neutron_arista.utils as arista_utils + + +class NeutronCreateAristaNetworkTest(neutron_tests.NeutronCreateNetworkTest): + """Test creating an Arista Neutron network through the API.""" + + @classmethod + def setUpClass(cls): + """Run class setup for running Neutron Arista tests.""" + super(NeutronCreateAristaNetworkTest, cls).setUpClass() + + logging.info('Waiting for Neutron to become ready...') + for attempt in tenacity.Retrying( + wait=tenacity.wait_fixed(5), # seconds + stop=tenacity.stop_after_attempt(12), + reraise=True): + with attempt: + cls.neutron_client.list_networks() + + def _test_400_additional_validation(self, expected_network_names): + """Arista-specific assertions for test_400_create_network. + + :type expected_network_names: List[str] + """ + logging.info("Querying Arista CVX's networks...") + actual_network_names = arista_utils.query_fixture_networks( + arista_utils.fixture_ip_addr()) + + # NOTE(lourot): the assertion name is misleading as it's not only + # checking the item count but also that all items are present in + # both lists, without checking the order. + self.assertCountEqual(actual_network_names, expected_network_names) diff --git a/zaza/openstack/charm_tests/neutron_arista/utils.py b/zaza/openstack/charm_tests/neutron_arista/utils.py new file mode 100644 index 0000000..3c68f33 --- /dev/null +++ b/zaza/openstack/charm_tests/neutron_arista/utils.py @@ -0,0 +1,67 @@ +# Copyright 2020 Canonical Ltd. +# +# 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. + +"""Common Arista-related utils.""" + +import json +import requests +import urllib3 +import zaza + +FIXTURE_APP_NAME = 'arista-virt-test-fixture' + + +def fixture_ip_addr(): + """Return the public IP address of the Arista test fixture.""" + return zaza.model.get_units(FIXTURE_APP_NAME)[0].public_address + + +_FIXTURE_LOGIN = 'admin' +_FIXTURE_PASSWORD = 'password123' + + +def query_fixture_networks(ip_addr): + """Query the Arista test fixture's list of networks.""" + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + session = requests.Session() + session.headers['Content-Type'] = 'application/json' + session.headers['Accept'] = 'application/json' + session.verify = False + session.auth = (_FIXTURE_LOGIN, _FIXTURE_PASSWORD) + + data = { + 'id': 'Zaza neutron-arista tests', + 'method': 'runCmds', + 'jsonrpc': '2.0', + 'params': { + 'timestamps': False, + 'format': 'json', + 'version': 1, + 'cmds': ['show openstack networks'] + } + } + + response = session.post( + 'https://{}/command-api/'.format(ip_addr), + data=json.dumps(data), + timeout=10 # seconds + ) + + result = [] + for _, region in response.json()['result'][0]['regions'].items(): + for _, tenant in region['tenants'].items(): + for _, network in tenant['tenantNetworks'].items(): + result.append(network['networkName']) + return result