Add functional tests
This commit is contained in:
parent
4e36a904c4
commit
c7930ba595
|
@ -5,3 +5,4 @@ build/
|
|||
func-results.json
|
||||
test-charm/
|
||||
interfaces
|
||||
__pycache__
|
||||
|
|
|
@ -1,29 +1,2 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
charm-tools>=2.4.4
|
||||
coverage>=3.6
|
||||
mock>=1.2
|
||||
flake8>=2.2.4,<=2.4.1
|
||||
os-testr>=0.4.1
|
||||
requests>=2.18.4
|
||||
# BEGIN: Amulet OpenStack Charm Helper Requirements
|
||||
# Liberty client lower constraints
|
||||
amulet>=1.14.3,<2.0;python_version=='2.7'
|
||||
bundletester>=0.6.1,<1.0;python_version=='2.7'
|
||||
python-ceilometerclient>=1.5.0
|
||||
python-cinderclient>=1.4.0
|
||||
python-glanceclient>=1.1.0
|
||||
python-heatclient>=0.8.0
|
||||
python-keystoneclient>=1.7.1
|
||||
python-neutronclient>=3.1.0
|
||||
python-novaclient>=2.30.1
|
||||
python-openstackclient>=1.7.0
|
||||
python-swiftclient>=2.6.0
|
||||
pika>=0.10.0,<1.0
|
||||
distro-info
|
||||
git+https://github.com/juju/charm-helpers.git#egg=charmhelpers
|
||||
# END: Amulet OpenStack Charm Helper Requirements
|
||||
# NOTE: workaround for 14.04 pip/tox
|
||||
pytz
|
||||
pyudev # for ceph-* charm unit tests (not mocked?)
|
||||
# zaza
|
||||
git+https://github.com/openstack-charmers/zaza.git#egg=zaza
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
series: bionic
|
||||
relations:
|
||||
- - nova-compute:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - neutron-gateway:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - neutron-gateway:amqp-nova
|
||||
- rabbitmq-server:amqp
|
||||
- - keystone:shared-db
|
||||
- mysql:shared-db
|
||||
- - cinder:identity-service
|
||||
- keystone:identity-service
|
||||
- - nova-cloud-controller:identity-service
|
||||
- keystone:identity-service
|
||||
- - glance:identity-service
|
||||
- keystone:identity-service
|
||||
- - neutron-api:identity-service
|
||||
- keystone:identity-service
|
||||
- - neutron-openvswitch:neutron-plugin-api
|
||||
- neutron-api:neutron-plugin-api
|
||||
- - cinder:shared-db
|
||||
- mysql:shared-db
|
||||
- - neutron-api:shared-db
|
||||
- mysql:shared-db
|
||||
- - cinder:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - neutron-api:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - neutron-gateway:neutron-plugin-api
|
||||
- neutron-api:neutron-plugin-api
|
||||
- - glance:shared-db
|
||||
- mysql:shared-db
|
||||
- - glance:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - nova-cloud-controller:image-service
|
||||
- glance:image-service
|
||||
- - nova-compute:image-service
|
||||
- glance:image-service
|
||||
- - nova-cloud-controller:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - nova-cloud-controller:quantum-network-service
|
||||
- neutron-gateway:quantum-network-service
|
||||
- - nova-compute:neutron-plugin
|
||||
- neutron-openvswitch:neutron-plugin
|
||||
- - neutron-openvswitch:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - nova-cloud-controller:shared-db
|
||||
- mysql:shared-db
|
||||
- - nova-cloud-controller:neutron-api
|
||||
- neutron-api:neutron-api
|
||||
- - nova-cloud-controller:cloud-compute
|
||||
- nova-compute:cloud-compute
|
||||
- - masakari:shared-db
|
||||
- mysql:shared-db
|
||||
- - masakari:amqp
|
||||
- rabbitmq-server:amqp
|
||||
- - masakari:identity-service
|
||||
- keystone:identity-service
|
||||
- - glance:ceph
|
||||
- ceph-mon:client
|
||||
- - ceph-mon:osd
|
||||
- ceph-osd:mon
|
||||
- - cinder:storage-backend
|
||||
- cinder-ceph:storage-backend
|
||||
- - cinder-ceph:ceph
|
||||
- ceph-mon:client
|
||||
- - cinder-ceph:ceph-access
|
||||
- nova-compute:ceph-access
|
||||
- - nova-compute:juju-info
|
||||
- masakari-monitors:container
|
||||
- - nova-compute:juju-info
|
||||
- hacluster:juju-info
|
||||
- - keystone:identity-credentials
|
||||
- masakari-monitors:identity-credentials
|
||||
applications:
|
||||
glance:
|
||||
charm: cs:~openstack-charmers-next/glance
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
worker-multiplier: 0.25
|
||||
cinder:
|
||||
charm: cs:~openstack-charmers-next/cinder
|
||||
num_units: 1
|
||||
options:
|
||||
block-device: "None"
|
||||
glance-api-version: 2
|
||||
keystone:
|
||||
charm: cs:~gnuoy/keystone-36
|
||||
series: bionic
|
||||
num_units: 1
|
||||
options:
|
||||
admin-password: openstack
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
worker-multiplier: 0.25
|
||||
mysql:
|
||||
charm: cs:~openstack-charmers-next/percona-cluster
|
||||
num_units: 1
|
||||
options:
|
||||
innodb-buffer-pool-size: 256M
|
||||
max-connections: 1000
|
||||
neutron-api:
|
||||
charm: cs:~openstack-charmers-next/neutron-api
|
||||
num_units: 1
|
||||
options:
|
||||
flat-network-providers: physnet1
|
||||
neutron-security-groups: true
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
worker-multiplier: 0.25
|
||||
neutron-gateway:
|
||||
charm: cs:~openstack-charmers-next/neutron-gateway
|
||||
num_units: 1
|
||||
options:
|
||||
bridge-mappings: physnet1:br-ex
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
worker-multiplier: 0.25
|
||||
neutron-openvswitch:
|
||||
charm: cs:~openstack-charmers-next/neutron-openvswitch
|
||||
num_units: 0
|
||||
nova-cloud-controller:
|
||||
charm: cs:~openstack-charmers-next/nova-cloud-controller
|
||||
num_units: 1
|
||||
options:
|
||||
network-manager: Neutron
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
worker-multiplier: 0.25
|
||||
debug: true
|
||||
nova-compute:
|
||||
charm: cs:~openstack-charmers-next/nova-compute
|
||||
num_units: 3
|
||||
constraints: mem=4G
|
||||
options:
|
||||
config-flags: default_ephemeral_format=ext4
|
||||
enable-live-migration: true
|
||||
enable-resize: true
|
||||
migration-auth-type: ssh
|
||||
openstack-origin: cloud:bionic-rocky
|
||||
debug: true
|
||||
cpu-model: kvm64
|
||||
cpu-mode: custom
|
||||
rabbitmq-server:
|
||||
charm: cs:~openstack-charmers-next/rabbitmq-server
|
||||
num_units: 1
|
||||
masakari:
|
||||
charm: cs:~gnuoy/masakari-2
|
||||
series: bionic
|
||||
num_units: 1
|
||||
options:
|
||||
openstack-origin: cloud:bionic-rocky/proposed
|
||||
ceph-mon:
|
||||
charm: ceph-mon
|
||||
num_units: 3
|
||||
options:
|
||||
expected-osd-count: 3
|
||||
ceph-osd:
|
||||
charm: ceph-osd
|
||||
constraints: mem=1G
|
||||
num_units: 3
|
||||
storage:
|
||||
osd-devices: cinder,40G
|
||||
cinder-ceph:
|
||||
charm: cinder-ceph
|
||||
masakari-monitors:
|
||||
charm: masakari-monitors
|
||||
series: bionic
|
||||
hacluster:
|
||||
charm: cs:~gnuoy/hacluster-6
|
||||
options:
|
||||
corosync_transport: unicast
|
||||
cluster_count: 3
|
|
@ -0,0 +1,13 @@
|
|||
charm_name: masakari-monitors
|
||||
tests:
|
||||
- tests.tests_masakari.MasakariTest
|
||||
configure:
|
||||
- zaza.charm_tests.glance.setup.add_cirros_image
|
||||
- zaza.charm_tests.glance.setup.add_lts_image
|
||||
- zaza.charm_tests.neutron.setup.basic_overcloud_network
|
||||
- zaza.charm_tests.nova.setup.create_flavors
|
||||
- zaza.charm_tests.nova.setup.manage_ssh_key
|
||||
gate_bundles:
|
||||
- bionic-rocky
|
||||
smoke_bundles:
|
||||
- bionic-rocky
|
|
@ -0,0 +1,230 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2019 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.
|
||||
|
||||
"""Encapsulate cinder-ns5 testing."""
|
||||
|
||||
import logging
|
||||
import tenacity
|
||||
import time
|
||||
|
||||
from openstack import connection
|
||||
|
||||
import zaza.model
|
||||
import zaza.charm_tests.test_utils as test_utils
|
||||
import zaza.utilities.openstack as openstack_utils
|
||||
import zaza.charm_tests.nova.utils as nova_utils
|
||||
|
||||
|
||||
class MasakariTest(test_utils.OpenStackBaseTest):
|
||||
"""Encapsulate NS5 tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running tests."""
|
||||
super(MasakariTest, cls).setUpClass()
|
||||
cls.keystone_session = openstack_utils.get_overcloud_keystone_session()
|
||||
cls.model_name = zaza.model.get_juju_model()
|
||||
cls.cinder_client = openstack_utils.get_cinder_session_client(
|
||||
cls.keystone_session)
|
||||
cls.nova_client = openstack_utils.get_nova_session_client(
|
||||
cls.keystone_session)
|
||||
cls.glance_client = openstack_utils.get_glance_session_client(
|
||||
cls.keystone_session)
|
||||
cls.neutron_client = openstack_utils.get_neutron_session_client(
|
||||
cls.keystone_session)
|
||||
|
||||
conn = connection.Connection(session=cls.keystone_session,
|
||||
interface='public',
|
||||
region_name='RegionOne')
|
||||
cls.masakari_client = conn.instance_ha
|
||||
|
||||
def launch_instance(self, instance_key, use_boot_volume=False,
|
||||
vm_name=None):
|
||||
"""Launch an instance.
|
||||
|
||||
:param instance_key: Key to collect associated config data with.
|
||||
:type instance_key: str
|
||||
"""
|
||||
# Collect resource information.
|
||||
vm_name = vm_name or time.strftime("%Y%m%d%H%M%S")
|
||||
image = self.nova_client.glance.find_image(
|
||||
instance_key)
|
||||
flavor = self.nova_client.flavors.find(
|
||||
name='m1.small')
|
||||
net = self.neutron_client.find_resource("network", "private")
|
||||
nics = [{'net-id': net.get('id')}]
|
||||
|
||||
if use_boot_volume:
|
||||
bdmv2 = [{
|
||||
'boot_index': '0',
|
||||
'uuid': image.id,
|
||||
'source_type': 'image',
|
||||
'volume_size': flavor.disk,
|
||||
'destination_type': 'volume',
|
||||
'delete_on_termination': True}]
|
||||
image = None
|
||||
|
||||
# Launch instance.
|
||||
logging.info('Launching instance {}'.format(vm_name))
|
||||
instance = self.nova_client.servers.create(
|
||||
name=vm_name,
|
||||
image=image,
|
||||
block_device_mapping_v2=bdmv2,
|
||||
flavor=flavor,
|
||||
key_name=nova_utils.KEYPAIR_NAME,
|
||||
nics=nics)
|
||||
|
||||
# Test Instance is ready.
|
||||
logging.info('Checking instance is active')
|
||||
openstack_utils.resource_reaches_status(
|
||||
self.nova_client.servers,
|
||||
instance.id,
|
||||
expected_status='ACTIVE')
|
||||
|
||||
logging.info('Checking cloud init is complete')
|
||||
openstack_utils.cloud_init_complete(
|
||||
self.nova_client,
|
||||
instance.id,
|
||||
'finished at')
|
||||
port = openstack_utils.get_ports_from_device_id(
|
||||
self.neutron_client,
|
||||
instance.id)[0]
|
||||
logging.info('Assigning floating ip.')
|
||||
ip = openstack_utils.create_floating_ip(
|
||||
self.neutron_client,
|
||||
"ext_net",
|
||||
port=port)['floating_ip_address']
|
||||
logging.info('Assigned floating IP {} to {}'.format(ip, vm_name))
|
||||
openstack_utils.ping_response(ip)
|
||||
|
||||
# Check ssh'ing to instance.
|
||||
# logging.info('Testing ssh access.')
|
||||
# openstack_utils.ssh_test(
|
||||
# username='ubuntu',
|
||||
# ip=ip,
|
||||
# vm_name=vm_name,
|
||||
# password=None,
|
||||
# privkey=openstack_utils.get_private_key(nova_utils.KEYPAIR_NAME))
|
||||
|
||||
def configure(self):
|
||||
try:
|
||||
self.masakari_client.create_segment(
|
||||
name='seg1',
|
||||
recovery_method='auto',
|
||||
service_type='COMPUTE')
|
||||
hypervisors = self.nova_client.hypervisors.list()
|
||||
segment_ids = [s.uuid for s in self.masakari_client.segments()]
|
||||
segment_ids = segment_ids * len(hypervisors)
|
||||
for hypervisor in hypervisors:
|
||||
target_segment = segment_ids.pop()
|
||||
hostname = hypervisor.hypervisor_hostname.split('.')[0]
|
||||
self.masakari_client.create_host(
|
||||
name=hostname,
|
||||
segment_id=target_segment,
|
||||
recovery_method='auto',
|
||||
control_attributes='SSH',
|
||||
type='COMPUTE')
|
||||
except:
|
||||
pass
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60),
|
||||
reraise=True, stop=tenacity.stop_after_attempt(80))
|
||||
def wait_for_server_migration(self, vm_name, original_hypervisor):
|
||||
server = self.nova_client.servers.find(name=vm_name)
|
||||
current_hypervisor = getattr(server, 'OS-EXT-SRV-ATTR:host')
|
||||
logging.info('{} is on {} in state {}'.format(
|
||||
vm_name,
|
||||
current_hypervisor,
|
||||
server.status))
|
||||
assert (original_hypervisor != current_hypervisor and
|
||||
server.status == 'ACTIVE')
|
||||
logging.info('SUCCESS {} has migrated to {}'.format(
|
||||
vm_name,
|
||||
current_hypervisor))
|
||||
|
||||
def svc_control(self, unit_name, action, services):
|
||||
logging.info('{} {} on {}'.format(action.title(), services, unit_name))
|
||||
cmds = []
|
||||
for svc in services:
|
||||
cmds.append("systemctl {} {}".format(action, svc))
|
||||
zaza.model.run_on_unit(
|
||||
unit_name, command=';'.join(cmds),
|
||||
model_name=self.model_name)
|
||||
|
||||
def enable_the_things(self):
|
||||
logging.info("Enabling all the things")
|
||||
# Start corosync et al
|
||||
for u in zaza.model.get_units(application_name='nova-compute'):
|
||||
self.svc_control(
|
||||
u.entity_id,
|
||||
'start',
|
||||
['corosync', 'pacemaker', 'nova-compute'])
|
||||
|
||||
# Enable nova-compute in nova
|
||||
for svc in self.nova_client.services.list():
|
||||
if svc.status == 'disabled':
|
||||
logging.info("Enabling {} on {}".format(svc.binary, svc.host))
|
||||
self.nova_client.services.enable(svc.host, svc.binary)
|
||||
|
||||
# Enable nova-compute in masakari
|
||||
for segment in self.masakari_client.segments():
|
||||
for host in self.masakari_client.hosts(segment_id=segment.uuid):
|
||||
if host.on_maintenance:
|
||||
logging.info("Removing maintenance mode from masakari "
|
||||
"host {}".format(host.uuid))
|
||||
self.masakari_client.update_host(
|
||||
host.uuid,
|
||||
segment_id=segment.uuid,
|
||||
**{'on_maintenance': False})
|
||||
|
||||
def test_instance_failover(self):
|
||||
self.configure()
|
||||
# Launch guest
|
||||
vm_name = 'zaza_test_instance_failover'
|
||||
try:
|
||||
server = self.nova_client.servers.find(name=vm_name)
|
||||
logging.info('Found existing guest')
|
||||
except:
|
||||
logging.info('Launching new guest')
|
||||
self.launch_instance(
|
||||
'bionic',
|
||||
use_boot_volume=True,
|
||||
vm_name=vm_name)
|
||||
server = self.nova_client.servers.find(name=vm_name)
|
||||
logging.info('Finding hosting hypervisor')
|
||||
server = self.nova_client.servers.find(name=vm_name)
|
||||
current_hypervisor = getattr(server, 'OS-EXT-SRV-ATTR:host')
|
||||
|
||||
logging.info('Simulate compute node shutdown')
|
||||
server = self.nova_client.servers.find(name=vm_name)
|
||||
guest_hypervisor = getattr(server, 'OS-EXT-SRV-ATTR:host')
|
||||
hypervisor_machine_number = guest_hypervisor.split('-')[-1]
|
||||
unit_name = [
|
||||
u.entity_id
|
||||
for u in zaza.model.get_units(application_name='nova-compute')
|
||||
if u.data['machine-id'] == hypervisor_machine_number][0]
|
||||
|
||||
# Simulate shutdown
|
||||
self.svc_control(
|
||||
unit_name,
|
||||
'stop',
|
||||
['corosync', 'pacemaker', 'nova-compute'])
|
||||
|
||||
# Wait for instance move
|
||||
self.wait_for_server_migration(vm_name, current_hypervisor)
|
||||
|
||||
# Bring things back
|
||||
self.enable_the_things()
|
99
src/tox.ini
99
src/tox.ini
|
@ -1,92 +1,35 @@
|
|||
# Classic charm: ./tox.ini
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos.
|
||||
[tox]
|
||||
envlist = pep8,py27,py35
|
||||
envlist = pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
CHARM_DIR={envdir}
|
||||
AMULET_SETUP_TIMEOUT=5400
|
||||
whitelist_externals = juju
|
||||
passenv = HOME TERM CS_API_* OS_* AMULET_*
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
commands = ostestr {posargs}
|
||||
whitelist_externals = juju
|
||||
passenv = HOME TERM AMULET_* CS_API_*
|
||||
|
||||
[testenv:py27]
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = /bin/true
|
||||
|
||||
[testenv:py35]
|
||||
basepython = python3.5
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
|
||||
[testenv:py36]
|
||||
basepython = python3.6
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = flake8 {posargs} hooks unit_tests tests actions lib
|
||||
charm-proof
|
||||
deps=charm-tools
|
||||
commands = charm-proof
|
||||
|
||||
[testenv:func-noop]
|
||||
basepython = python3
|
||||
commands =
|
||||
true
|
||||
|
||||
[testenv:func]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model
|
||||
|
||||
[testenv:func-smoke]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --smoke
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:func27-noop]
|
||||
# DRY RUN - For Debug
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" -n --no-destroy
|
||||
|
||||
[testenv:func27]
|
||||
# Charm Functional Test
|
||||
# Run all gate tests which are +x (expected to always pass)
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "gate-*" --no-destroy
|
||||
|
||||
[testenv:func27-smoke]
|
||||
# Charm Functional Test
|
||||
# Run a specific test as an Amulet smoke test (expected to always pass)
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
bundletester -vl DEBUG -r json -o func-results.json gate-basic-bionic-rocky --no-destroy
|
||||
|
||||
[testenv:func27-dfs]
|
||||
# Charm Functional Test
|
||||
# Run all deploy-from-source tests which are +x (may not always pass!)
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dfs-*" --no-destroy
|
||||
|
||||
[testenv:func27-dev]
|
||||
# Charm Functional Test
|
||||
# Run all development test targets which are +x (may not always pass!)
|
||||
basepython = python2.7
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
bundletester -vl DEBUG -r json -o func-results.json --test-pattern "dev-*" --no-destroy
|
||||
|
||||
[flake8]
|
||||
ignore = E402,E226
|
||||
exclude = */charmhelpers
|
||||
|
|
Loading…
Reference in New Issue