Merge branch 'master' into osd-remove-test
This commit is contained in:
@@ -345,6 +345,20 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
|
||||
openstack_utils.get_images_by_name(glance_client, 'frank'),
|
||||
[])
|
||||
|
||||
def test_get_volumes_by_name(self):
|
||||
volume_mock1 = mock.MagicMock()
|
||||
volume_mock1.name = 'bob'
|
||||
volume_mock2 = mock.MagicMock()
|
||||
volume_mock2.name = 'bill'
|
||||
cinder_client = mock.MagicMock()
|
||||
cinder_client.volumes.list.return_value = [volume_mock1, volume_mock2]
|
||||
self.assertEqual(
|
||||
openstack_utils.get_volumes_by_name(cinder_client, 'bob'),
|
||||
[volume_mock1])
|
||||
self.assertEqual(
|
||||
openstack_utils.get_volumes_by_name(cinder_client, 'frank'),
|
||||
[])
|
||||
|
||||
def test_find_cirros_image(self):
|
||||
urllib_opener_mock = mock.MagicMock()
|
||||
self.patch_object(openstack_utils, "get_urllib_opener")
|
||||
@@ -942,6 +956,34 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
|
||||
self.assertEqual(expected, result)
|
||||
self._get_os_rel_pair.assert_called_once_with(application='myapp')
|
||||
|
||||
def test_get_keystone_ip__vip(self):
|
||||
self.patch_object(openstack_utils, "get_application_config_option")
|
||||
self.patch_object(openstack_utils.model, "get_units")
|
||||
unit1 = mock.Mock(public_address='5.6.7.8')
|
||||
self.get_application_config_option.return_value = "1.2.3.4"
|
||||
self.get_units.return_value = [unit1]
|
||||
|
||||
self.assertEqual(
|
||||
openstack_utils.get_keystone_ip(model_name='some-model'),
|
||||
'1.2.3.4')
|
||||
self.get_application_config_option.assert_called_once_with(
|
||||
'keystone', 'vip', model_name='some-model')
|
||||
self.get_application_config_option.return_value = " 1.2.3.4 11"
|
||||
self.assertEqual(openstack_utils.get_keystone_ip(), '1.2.3.4')
|
||||
|
||||
def test_get_keystone_ip__from_unit(self):
|
||||
self.patch_object(openstack_utils, "get_application_config_option")
|
||||
self.patch_object(openstack_utils.model, "get_units")
|
||||
self.patch_object(openstack_utils.model, 'get_unit_public_address')
|
||||
mock_unit1 = mock.Mock()
|
||||
self.get_unit_public_address.return_value = '5.6.7.8'
|
||||
self.get_application_config_option.return_value = None
|
||||
self.get_units.return_value = [mock_unit1]
|
||||
|
||||
self.assertEqual(openstack_utils.get_keystone_ip(), '5.6.7.8')
|
||||
self.get_units.assert_called_once_with('keystone', model_name=None)
|
||||
self.get_unit_public_address.assert_called_once_with(mock_unit1)
|
||||
|
||||
def test_get_keystone_api_version(self):
|
||||
self.patch_object(openstack_utils, "get_current_os_versions")
|
||||
self.patch_object(openstack_utils, "get_application_config_option")
|
||||
|
||||
@@ -91,12 +91,20 @@ class TestSwiftUtils(ut_utils.BaseTestCase):
|
||||
self.patch_object(juju_utils, 'get_full_juju_status')
|
||||
self.patch_object(zaza.model, 'get_application_config')
|
||||
self.patch_object(zaza.model, 'get_units')
|
||||
self.patch_object(zaza.model, 'get_unit_public_address')
|
||||
|
||||
def _get_unit_public_address(u):
|
||||
return u.public_address
|
||||
|
||||
self.get_unit_public_address.side_effect = _get_unit_public_address
|
||||
|
||||
juju_status = mock.MagicMock()
|
||||
juju_status.applications = {}
|
||||
self.get_full_juju_status.return_value = juju_status
|
||||
|
||||
for app_name, units in app_units.items():
|
||||
expected_topology[units[0].public_address]['unit'] = units[0]
|
||||
ip = units[0].public_address
|
||||
expected_topology[ip]['unit'] = units[0]
|
||||
|
||||
app_config = {}
|
||||
for app_name in app_units.keys():
|
||||
|
||||
@@ -116,6 +116,21 @@ class TestUpgradeUtils(ut_utils.BaseTestCase):
|
||||
self.assertEqual(
|
||||
actual,
|
||||
expected)
|
||||
# test that, at focal, there are no database services.
|
||||
expected = [
|
||||
('Database Services', []),
|
||||
('Stateful Services', []),
|
||||
('Core Identity', []),
|
||||
('Control Plane', ['cinder']),
|
||||
('Data Plane', ['nova-compute']),
|
||||
('sweep_up', ['ntp'])]
|
||||
actual = openstack_upgrade.get_series_upgrade_groups(
|
||||
target_series='focal')
|
||||
pprint.pprint(expected)
|
||||
pprint.pprint(actual)
|
||||
self.assertEqual(
|
||||
actual,
|
||||
expected)
|
||||
|
||||
def test_extract_charm_name_from_url(self):
|
||||
self.assertEqual(
|
||||
|
||||
@@ -160,5 +160,10 @@ class CeilometerTest(test_utils.OpenStackBaseTest):
|
||||
Pause service and check services are stopped then resume and check
|
||||
they are started.
|
||||
"""
|
||||
if self.application_name == 'ceilometer-agent':
|
||||
logging.info("ceilometer-agent doesn't have pause/resume actions "
|
||||
"anymore, skipping")
|
||||
return
|
||||
|
||||
with self.pause_resume(self.restartable_services):
|
||||
logging.info("Testing pause and resume")
|
||||
|
||||
@@ -48,4 +48,5 @@ def set_grafana_url(model_name=None):
|
||||
'ceph-dashboard',
|
||||
{
|
||||
'grafana-api-url': "https://{}:3000".format(
|
||||
unit.public_address)})
|
||||
zaza.model.get_unit_public_address(unit))
|
||||
})
|
||||
|
||||
@@ -103,11 +103,13 @@ class CephDashboardTest(test_utils.BaseCharmTest):
|
||||
units = zaza.model.get_units(self.application_name)
|
||||
for unit in units:
|
||||
r = self._run_request_get(
|
||||
'https://{}:8443'.format(unit.public_address),
|
||||
'https://{}:8443'.format(
|
||||
zaza.model.get_unit_public_address(unit)),
|
||||
verify=self.local_ca_cert,
|
||||
allow_redirects=False)
|
||||
if r.status_code == requests.codes.ok:
|
||||
return 'https://{}:8443'.format(unit.public_address)
|
||||
return 'https://{}:8443'.format(
|
||||
zaza.model.get_unit_public_address(unit))
|
||||
|
||||
def test_dashboard_units(self):
|
||||
"""Check dashboard units are configured correctly."""
|
||||
@@ -116,10 +118,11 @@ class CephDashboardTest(test_utils.BaseCharmTest):
|
||||
rcs = collections.defaultdict(list)
|
||||
for unit in units:
|
||||
r = self._run_request_get(
|
||||
'https://{}:8443'.format(unit.public_address),
|
||||
'https://{}:8443'.format(
|
||||
zaza.model.get_unit_public_address(unit)),
|
||||
verify=verify,
|
||||
allow_redirects=False)
|
||||
rcs[r.status_code].append(unit.public_address)
|
||||
rcs[r.status_code].append(zaza.model.get_unit_public_address(unit))
|
||||
self.assertEqual(len(rcs[requests.codes.ok]), 1)
|
||||
self.assertEqual(len(rcs[requests.codes.see_other]), len(units) - 1)
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class CephISCSIGatewayTest(test_utils.BaseCharmTest):
|
||||
ctxt['gateway_units'] = [
|
||||
{
|
||||
'entity_id': u.entity_id,
|
||||
'ip': u.public_address,
|
||||
'ip': zaza.model.get_unit_public_address(u),
|
||||
'hostname': host_names[u.entity_id]}
|
||||
for u in zaza.model.get_units('ceph-iscsi')]
|
||||
ctxt['gw_ip'] = sorted([g['ip'] for g in ctxt['gateway_units']])[0]
|
||||
|
||||
@@ -35,7 +35,6 @@ import zaza.openstack.utilities.exceptions as zaza_exceptions
|
||||
import zaza.openstack.utilities.generic as zaza_utils
|
||||
import zaza.utilities.juju as juju_utils
|
||||
import zaza.openstack.utilities.openstack as zaza_openstack
|
||||
import zaza.openstack.utilities.juju as zaza_juju
|
||||
|
||||
|
||||
class CephLowLevelTest(test_utils.OpenStackBaseTest):
|
||||
@@ -124,7 +123,7 @@ class CephRelationTest(test_utils.OpenStackBaseTest):
|
||||
remote_unit_name = 'ceph-mon/0'
|
||||
relation_name = 'osd'
|
||||
remote_unit = zaza_model.get_unit_from_name(remote_unit_name)
|
||||
remote_ip = remote_unit.public_address
|
||||
remote_ip = zaza_model.get_unit_public_address(remote_unit)
|
||||
relation = juju_utils.get_relation_from_unit(
|
||||
unit_name,
|
||||
remote_unit_name,
|
||||
@@ -145,7 +144,7 @@ class CephRelationTest(test_utils.OpenStackBaseTest):
|
||||
unit_name = 'ceph-osd/0'
|
||||
relation_name = 'osd'
|
||||
remote_unit = zaza_model.get_unit_from_name(remote_unit_name)
|
||||
remote_ip = remote_unit.public_address
|
||||
remote_ip = zaza_model.get_unit_public_address(remote_unit)
|
||||
cmd = 'leader-get fsid'
|
||||
result = zaza_model.run_on_unit(remote_unit_name, cmd)
|
||||
fsid = result.get('Stdout').strip()
|
||||
@@ -546,20 +545,28 @@ class CephTest(test_utils.OpenStackBaseTest):
|
||||
)
|
||||
logging.debug('OK')
|
||||
|
||||
def _get_local_osd_id(self, unit):
|
||||
def get_local_osd_id(self, unit):
|
||||
"""Get the OSD id for a unit."""
|
||||
ret = zaza_model.run_on_unit(unit,
|
||||
'ceph-volume lvm list --format=json')
|
||||
local = list(json.loads(ret['Stdout']))[-1]
|
||||
return local if local.startswith('osd.') else 'osd.' + local
|
||||
|
||||
def get_num_osds(self, osd):
|
||||
"""Compute the number of active OSD's."""
|
||||
result = zaza_model.run_on_unit(osd, 'ceph osd stat --format=json')
|
||||
result = json.loads(result['Stdout'])
|
||||
return int(result['num_osds'])
|
||||
|
||||
def test_cache_device(self):
|
||||
"""Test replacing a disk in use."""
|
||||
logging.info('Running add-disk action with a caching device')
|
||||
mon = next(iter(zaza_model.get_units('ceph-mon'))).entity_id
|
||||
osds = [x.entity_id for x in zaza_model.get_units('ceph-osd')]
|
||||
params = []
|
||||
for unit in osds:
|
||||
zaza_juju.add_storage(unit, 'cache-devices', 'cinder', 10)
|
||||
loop_dev = zaza_juju.add_loop_device(unit, 10).get('Stdout')
|
||||
zaza_model.add_storage(unit, 'cache-devices', 'cinder', 10)
|
||||
loop_dev = zaza_utils.add_loop_device(unit, 10)
|
||||
params.append({'unit': unit})
|
||||
action_obj = zaza_model.run_action(
|
||||
unit_name=unit,
|
||||
@@ -572,13 +579,13 @@ class CephTest(test_utils.OpenStackBaseTest):
|
||||
|
||||
logging.info('Removing previously added disks')
|
||||
for param in params:
|
||||
osd_id = self._get_local_osd_id(param['unit'])
|
||||
osd_id = self.get_local_osd_id(param['unit'])
|
||||
param.update({'osd-id': osd_id})
|
||||
action_obj = zaza_model.run_action(
|
||||
unit_name=param['unit'],
|
||||
action_name='remove-disk',
|
||||
action_params={'osd-ids': osd_id, 'timeout': 5,
|
||||
'format': 'json', 'purge': True}
|
||||
'format': 'json', 'purge': False}
|
||||
)
|
||||
zaza_utils.assertActionRanOK(action_obj)
|
||||
results = json.loads(action_obj.data['results']['message'])
|
||||
@@ -599,6 +606,7 @@ class CephTest(test_utils.OpenStackBaseTest):
|
||||
)
|
||||
zaza_utils.assertActionRanOK(action_obj)
|
||||
zaza_model.wait_for_application_states()
|
||||
self.assertEqual(len(osds) * 2, self.get_num_osds(mon))
|
||||
|
||||
|
||||
class CephRGWTest(test_utils.OpenStackBaseTest):
|
||||
@@ -861,7 +869,9 @@ class CephPrometheusTest(unittest.TestCase):
|
||||
unit = zaza_model.get_unit_from_name(
|
||||
zaza_model.get_lead_unit_name('prometheus2'))
|
||||
self.assertEqual(
|
||||
'3', _get_mon_count_from_prometheus(unit.public_address))
|
||||
'3',
|
||||
_get_mon_count_from_prometheus(
|
||||
zaza_model.get_unit_public_address(unit)))
|
||||
|
||||
|
||||
class CephPoolConfig(Exception):
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2022 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 cinder backends."""
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2021 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 backend tests."""
|
||||
|
||||
import uuid
|
||||
|
||||
import zaza.model
|
||||
import zaza.openstack.charm_tests.test_utils as test_utils
|
||||
import zaza.openstack.utilities.openstack as openstack_utils
|
||||
|
||||
|
||||
class CinderBackendTest(test_utils.OpenStackBaseTest):
|
||||
"""Encapsulate cinder backend tests."""
|
||||
|
||||
expected_config_content = {}
|
||||
backend_name = ""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running tests."""
|
||||
super(CinderBackendTest, 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)
|
||||
|
||||
def test_cinder_config(self):
|
||||
"""Test that configuration options match our expectations."""
|
||||
zaza.model.run_on_leader(
|
||||
'cinder',
|
||||
'sudo cp /etc/cinder/cinder.conf /tmp/')
|
||||
zaza.model.block_until_oslo_config_entries_match(
|
||||
'cinder',
|
||||
'/tmp/cinder.conf',
|
||||
self.expected_config_content,
|
||||
timeout=2)
|
||||
|
||||
def test_create_volume(self):
|
||||
"""Test creating a volume with basic configuration."""
|
||||
test_vol_name = "zaza{}".format(uuid.uuid1().fields[0])
|
||||
vol_new = self.cinder_client.volumes.create(
|
||||
name=test_vol_name,
|
||||
size='1')
|
||||
try:
|
||||
openstack_utils.resource_reaches_status(
|
||||
self.cinder_client.volumes,
|
||||
vol_new.id,
|
||||
wait_iteration_max_time=12000,
|
||||
stop_after_attempt=5,
|
||||
expected_status='available',
|
||||
msg='Volume status wait')
|
||||
test_vol = self.cinder_client.volumes.find(name=test_vol_name)
|
||||
self.assertEqual(
|
||||
getattr(test_vol, 'os-vol-host-attr:host').split('#')[0],
|
||||
'cinder@{}'.format(self.backend_name))
|
||||
finally:
|
||||
self.cinder_client.volumes.delete(vol_new)
|
||||
@@ -16,62 +16,19 @@
|
||||
|
||||
"""Encapsulate cinder-netapp testing."""
|
||||
|
||||
import uuid
|
||||
|
||||
import zaza.model
|
||||
import zaza.openstack.charm_tests.test_utils as test_utils
|
||||
import zaza.openstack.utilities.openstack as openstack_utils
|
||||
from zaza.openstack.charm_tests.cinder_backend.tests import CinderBackendTest
|
||||
|
||||
|
||||
class CinderNetAppTest(test_utils.OpenStackBaseTest):
|
||||
class CinderNetAppTest(CinderBackendTest):
|
||||
"""Encapsulate netapp tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running tests."""
|
||||
super(CinderNetAppTest, 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)
|
||||
backend_name = 'cinder-netapp'
|
||||
|
||||
def test_cinder_config(self):
|
||||
"""Test that configuration options match our expectations."""
|
||||
expected_contents = {
|
||||
'cinder-netapp': {
|
||||
'netapp_storage_family': ['ontap_cluster'],
|
||||
'netapp_storage_protocol': ['iscsi'],
|
||||
'volume_backend_name': ['cinder_netapp'],
|
||||
'volume_driver':
|
||||
expected_config_content = {
|
||||
'cinder-netapp': {
|
||||
'netapp_storage_family': ['ontap_cluster'],
|
||||
'netapp_storage_protocol': ['iscsi'],
|
||||
'volume_backend_name': ['cinder_netapp'],
|
||||
'volume_driver':
|
||||
['cinder.volume.drivers.netapp.common.NetAppDriver'],
|
||||
}}
|
||||
|
||||
zaza.model.run_on_leader(
|
||||
'cinder',
|
||||
'sudo cp /etc/cinder/cinder.conf /tmp/')
|
||||
zaza.model.block_until_oslo_config_entries_match(
|
||||
'cinder',
|
||||
'/tmp/cinder.conf',
|
||||
expected_contents,
|
||||
timeout=2)
|
||||
|
||||
def test_create_volume(self):
|
||||
"""Test creating a volume with basic configuration."""
|
||||
test_vol_name = "zaza{}".format(uuid.uuid1().fields[0])
|
||||
vol_new = self.cinder_client.volumes.create(
|
||||
name=test_vol_name,
|
||||
size='1')
|
||||
try:
|
||||
openstack_utils.resource_reaches_status(
|
||||
self.cinder_client.volumes,
|
||||
vol_new.id,
|
||||
wait_iteration_max_time=12000,
|
||||
stop_after_attempt=5,
|
||||
expected_status='available',
|
||||
msg='Volume status wait')
|
||||
test_vol = self.cinder_client.volumes.find(name=test_vol_name)
|
||||
self.assertEqual(
|
||||
getattr(test_vol, 'os-vol-host-attr:host').split('#')[0],
|
||||
'cinder@cinder-netapp')
|
||||
finally:
|
||||
self.cinder_client.volumes.delete(vol_new)
|
||||
}}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"""Encapsulate glance testing."""
|
||||
|
||||
import logging
|
||||
import math
|
||||
|
||||
import boto3
|
||||
import zaza.model as model
|
||||
@@ -222,3 +223,46 @@ class GlanceExternalS3Test(test_utils.OpenStackBaseTest):
|
||||
)
|
||||
self.assertEqual(image["size"], response["ContentLength"])
|
||||
openstack_utils.delete_image(self.glance_client, image["id"])
|
||||
|
||||
|
||||
class GlanceCinderBackendTest(test_utils.OpenStackBaseTest):
|
||||
"""Encapsulate glance tests using cinder backend."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Run class setup for running glance tests with cinder backend."""
|
||||
super(GlanceCinderBackendTest, cls).setUpClass()
|
||||
cls.glance_client = openstack_utils.get_glance_session_client(
|
||||
cls.keystone_session)
|
||||
cls.cinder_client = openstack_utils.get_cinder_session_client(
|
||||
cls.keystone_session)
|
||||
|
||||
def test_100_create_delete_image(self):
|
||||
"""Create an image and do a simple validation of it.
|
||||
|
||||
Validate the size of the image in both Glance API and Cinder API.
|
||||
"""
|
||||
image_name = "zaza-cinder-test-image"
|
||||
openstack_utils.create_image(
|
||||
glance=self.glance_client,
|
||||
image_url=openstack_utils.find_cirros_image(arch="x86_64"),
|
||||
image_name=image_name,
|
||||
backend="cinder",
|
||||
)
|
||||
images = openstack_utils.get_images_by_name(
|
||||
self.glance_client, image_name)
|
||||
self.assertEqual(len(images), 1)
|
||||
image = images[0]
|
||||
|
||||
volume_name = 'image-'+image["id"]
|
||||
volumes = openstack_utils.get_volumes_by_name(
|
||||
self.cinder_client, volume_name)
|
||||
self.assertEqual(len(volumes), 1)
|
||||
volume = volumes[0]
|
||||
|
||||
logging.info(
|
||||
"Checking glance image size {} matches volume size {} "
|
||||
"GB".format(image["size"], volume.size))
|
||||
image_size_in_gb = int(math.ceil(float(image["size"]) / 1024 ** 3))
|
||||
self.assertEqual(image_size_in_gb, volume.size)
|
||||
openstack_utils.delete_image(self.glance_client, image["id"])
|
||||
|
||||
@@ -42,5 +42,5 @@ def setup_ganesha_share_type(manila_client=None):
|
||||
extra_specs={
|
||||
'vendor_name': 'Ceph',
|
||||
'storage_protocol': 'NFS',
|
||||
'snapshot_support': False,
|
||||
'snapshot_support': True,
|
||||
})
|
||||
|
||||
@@ -90,7 +90,7 @@ class MySQLBaseTest(test_utils.OpenStackBaseTest):
|
||||
_primary_ip = _primary_ip.split(':')[0]
|
||||
units = zaza.model.get_units(self.application_name)
|
||||
for unit in units:
|
||||
if _primary_ip in unit.public_address:
|
||||
if _primary_ip in zaza.model.get_unit_public_address(unit):
|
||||
return unit
|
||||
|
||||
def get_blocked_mysql_routers(self):
|
||||
@@ -802,6 +802,7 @@ class MySQLInnoDBClusterScaleTest(MySQLBaseTest):
|
||||
leader, nons = generic_utils.get_leaders_and_non_leaders(
|
||||
self.application_name)
|
||||
leader_unit = zaza.model.get_unit_from_name(leader)
|
||||
leader_unit_ip = zaza.model.get_unit_public_address(leader_unit)
|
||||
|
||||
# Wait until we are idle in the hopes clients are not running
|
||||
# update-status hooks
|
||||
@@ -821,12 +822,12 @@ class MySQLInnoDBClusterScaleTest(MySQLBaseTest):
|
||||
|
||||
logging.info(
|
||||
"Removing old unit from cluster: {} "
|
||||
.format(leader_unit.public_address))
|
||||
.format(leader_unit_ip))
|
||||
action = zaza.model.run_action(
|
||||
nons[0],
|
||||
"remove-instance",
|
||||
action_params={
|
||||
"address": leader_unit.public_address,
|
||||
"address": leader_unit_ip,
|
||||
"force": True})
|
||||
assert action.data.get("results") is not None, (
|
||||
"Remove instance action failed: No results: {}"
|
||||
@@ -877,6 +878,8 @@ class MySQLInnoDBClusterScaleTest(MySQLBaseTest):
|
||||
leader, nons = generic_utils.get_leaders_and_non_leaders(
|
||||
self.application_name)
|
||||
non_leader_unit = zaza.model.get_unit_from_name(nons[0])
|
||||
non_leader_unit_ip = zaza.model.get_unit_public_address(
|
||||
non_leader_unit)
|
||||
|
||||
# Wait until we are idle in the hopes clients are not running
|
||||
# update-status hooks
|
||||
@@ -897,12 +900,12 @@ class MySQLInnoDBClusterScaleTest(MySQLBaseTest):
|
||||
|
||||
logging.info(
|
||||
"Removing old unit from cluster: {} "
|
||||
.format(non_leader_unit.public_address))
|
||||
.format(non_leader_unit_ip))
|
||||
action = zaza.model.run_action(
|
||||
leader,
|
||||
"remove-instance",
|
||||
action_params={
|
||||
"address": non_leader_unit.public_address,
|
||||
"address": non_leader_unit_ip,
|
||||
"force": True})
|
||||
assert action.data.get("results") is not None, (
|
||||
"Remove instance action failed: No results: {}"
|
||||
@@ -925,7 +928,7 @@ class MySQLInnoDBClusterPartitionTest(MySQLBaseTest):
|
||||
no_of_units = len(mysql_units)
|
||||
for index, unit in enumerate(mysql_units):
|
||||
next_unit = mysql_units[(index+1) % no_of_units]
|
||||
ip_address = next_unit.public_address
|
||||
ip_address = zaza.model.get_unit_public_address(next_unit)
|
||||
cmd = "sudo iptables -A INPUT -s {} -j DROP".format(ip_address)
|
||||
zaza.model.async_run_on_unit(unit, cmd)
|
||||
|
||||
@@ -949,7 +952,7 @@ class MySQLInnoDBClusterPartitionTest(MySQLBaseTest):
|
||||
leader_unit.entity_id,
|
||||
"force-quorum-using-partition-of",
|
||||
action_params={
|
||||
"address": leader_unit.public_address,
|
||||
"address": zaza.model.get_unit_public_address(leader_unit),
|
||||
'i-really-mean-it': True
|
||||
})
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ from zaza.openstack.configure import (
|
||||
from zaza.openstack.utilities import (
|
||||
cli as cli_utils,
|
||||
generic as generic_utils,
|
||||
juju as juju_utils,
|
||||
openstack as openstack_utils,
|
||||
)
|
||||
|
||||
import zaza.utilities.juju as juju_utils
|
||||
|
||||
import zaza.charm_lifecycle.utils as lifecycle_utils
|
||||
|
||||
|
||||
|
||||
@@ -492,14 +492,11 @@ class NeutronApiTest(NeutronCreateNetworkTest):
|
||||
Pause service and check services are stopped then resume and check
|
||||
they are started
|
||||
"""
|
||||
bionic_stein = openstack_utils.get_os_release('bionic_stein')
|
||||
if openstack_utils.get_os_release() >= bionic_stein:
|
||||
pgrep_full = True
|
||||
else:
|
||||
pgrep_full = False
|
||||
with self.pause_resume(
|
||||
["neutron-server", "apache2", "haproxy"],
|
||||
pgrep_full=pgrep_full):
|
||||
["/usr/bin/neutron-server",
|
||||
"/usr/sbin/apache2",
|
||||
"/usr/sbin/haproxy"],
|
||||
pgrep_full=True):
|
||||
logging.info("Testing pause resume")
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ PLUGIN_APP_NAME = 'neutron-api-plugin-arista'
|
||||
|
||||
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
|
||||
return zaza.model.get_unit_public_address(
|
||||
zaza.model.get_units(FIXTURE_APP_NAME)[0])
|
||||
|
||||
|
||||
_FIXTURE_LOGIN = 'admin'
|
||||
|
||||
@@ -461,7 +461,7 @@ class NovaCloudControllerActionTest(test_utils.OpenStackBaseTest):
|
||||
if juju_az:
|
||||
zone = juju_az
|
||||
|
||||
juju_units_az_map[unit.public_address] = zone
|
||||
juju_units_az_map[zaza.model.get_unit_public_address(unit)] = zone
|
||||
continue
|
||||
|
||||
session = openstack_utils.get_overcloud_keystone_session()
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"""Code for configuring octavia."""
|
||||
|
||||
import os
|
||||
import re
|
||||
import base64
|
||||
import logging
|
||||
|
||||
@@ -141,6 +142,63 @@ def configure_octavia():
|
||||
'octavia',
|
||||
'configure-resources',
|
||||
action_params={})
|
||||
# When bug #1964117 is fix released for all affected releases this call can
|
||||
# be removed.
|
||||
bug_1964117_workaround()
|
||||
|
||||
|
||||
def disable_ohm_port_security():
|
||||
"""Disable port security on the health manager ports on octavia units."""
|
||||
keystone_session = openstack.get_overcloud_keystone_session()
|
||||
neutron_client = openstack.get_neutron_session_client(
|
||||
keystone_session)
|
||||
ports = [
|
||||
p
|
||||
for p in neutron_client.list_ports()['ports']
|
||||
if re.match('octavia-health-manager-.*-listen-port', p['name'])]
|
||||
for port in ports:
|
||||
neutron_client.update_port(
|
||||
port['id'],
|
||||
{
|
||||
'port':
|
||||
{
|
||||
'port_security_enabled': False,
|
||||
'security_groups': []}})
|
||||
|
||||
|
||||
def bug_1964117_workaround():
|
||||
"""Apply Bug #1964117 if allowed."""
|
||||
if openstack.ovn_present():
|
||||
# Issue only known to affect ml2 ovs so if do not apply work around
|
||||
# to ovn deploys.
|
||||
return
|
||||
allow_pkg_list = ['2.16.0-0ubuntu2.1~cloud0']
|
||||
allow_release_list = ['focal_xena']
|
||||
_allow_release_list = [
|
||||
openstack.get_os_release(r)
|
||||
for r in allow_release_list
|
||||
]
|
||||
current_release = openstack.get_os_release()
|
||||
if current_release in _allow_release_list:
|
||||
cmd_out = zaza.model.run_on_leader(
|
||||
'octavia',
|
||||
"""dpkg -l | awk '/openvswitch-switch/ {print $3;}'""")
|
||||
pkg_version = cmd_out['Stdout'].strip()
|
||||
if pkg_version in allow_pkg_list:
|
||||
logging.info('Disabling port security to work around bug #1964117')
|
||||
disable_ohm_port_security()
|
||||
else:
|
||||
msg = (
|
||||
"Detected Xena deploy and package version {} is not in the "
|
||||
"allow list {}. If you believe that bug #1964117 has been "
|
||||
"resolved please remove the call to this function. If the "
|
||||
"new package does not resolve bug #1964117 then please add "
|
||||
"the new package version to the 'allow_pkg_list' defined at "
|
||||
"the start of this function. If changes are required please "
|
||||
"raise a PR againt "
|
||||
"https://github.com/openstack-charmers/zaza-openstack-tests"
|
||||
"".format(pkg_version, allow_pkg_list))
|
||||
raise Exception(msg)
|
||||
|
||||
|
||||
def centralized_fip_network():
|
||||
|
||||
@@ -29,6 +29,10 @@ import zaza.openstack.utilities.openstack as openstack_utils
|
||||
|
||||
from zaza.openstack.utilities import generic as generic_utils
|
||||
from zaza.openstack.utilities import ObjectRetrierWraps
|
||||
from zaza.openstack.utilities.exceptions import (
|
||||
LoadBalancerUnexpectedState,
|
||||
LoadBalancerUnrecoverableError,
|
||||
)
|
||||
|
||||
LBAAS_ADMIN_ROLE = 'load-balancer_admin'
|
||||
|
||||
@@ -279,24 +283,35 @@ class LBAASv2Test(test_utils.OpenStackBaseTest):
|
||||
super(LBAASv2Test, self).resource_cleanup()
|
||||
|
||||
@staticmethod
|
||||
@tenacity.retry(retry=tenacity.retry_if_exception_type(AssertionError),
|
||||
wait=tenacity.wait_fixed(1), reraise=True,
|
||||
stop=tenacity.stop_after_delay(900))
|
||||
@tenacity.retry(
|
||||
retry=tenacity.retry_if_exception_type(LoadBalancerUnexpectedState),
|
||||
wait=tenacity.wait_fixed(1), reraise=True,
|
||||
stop=tenacity.stop_after_delay(900))
|
||||
def wait_for_lb_resource(octavia_show_func, resource_id,
|
||||
provisioning_status=None, operating_status=None):
|
||||
"""Wait for loadbalancer resource to reach expected status."""
|
||||
provisioning_status = provisioning_status or 'ACTIVE'
|
||||
resp = octavia_show_func(resource_id)
|
||||
logging.info(resp['provisioning_status'])
|
||||
assert resp['provisioning_status'] == provisioning_status, (
|
||||
'load balancer resource has not reached '
|
||||
'expected provisioning status: {}'
|
||||
.format(resp))
|
||||
logging.info("Current provisioning status: {}, waiting for {}"
|
||||
.format(resp['provisioning_status'], provisioning_status))
|
||||
|
||||
msg = ('load balancer resource has not reached '
|
||||
'expected provisioning status: {}'.format(resp))
|
||||
|
||||
# ERROR is a final state, once it's reached there is no reason to keep
|
||||
# retrying and delaying the failure.
|
||||
if resp['provisioning_status'] == 'ERROR':
|
||||
raise LoadBalancerUnrecoverableError(msg)
|
||||
elif resp['provisioning_status'] != provisioning_status:
|
||||
raise LoadBalancerUnexpectedState(msg)
|
||||
|
||||
if operating_status:
|
||||
logging.info(resp['operating_status'])
|
||||
assert resp['operating_status'] == operating_status, (
|
||||
'load balancer resource has not reached '
|
||||
'expected operating status: {}'.format(resp))
|
||||
logging.info('Current operating status: {}, waiting for {}'
|
||||
.format(resp['operating_status'], operating_status))
|
||||
if not resp['operating_status'] == operating_status:
|
||||
raise LoadBalancerUnexpectedState((
|
||||
'load balancer resource has not reached '
|
||||
'expected operating status: {}'.format(resp)))
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ class OpenStackDashboardBase():
|
||||
else:
|
||||
unit = zaza_model.get_unit_from_name(
|
||||
zaza_model.get_lead_unit_name(self.application_name))
|
||||
ip = unit.public_address
|
||||
ip = zaza_model.get_unit_public_address(unit)
|
||||
|
||||
logging.debug("Dashboard ip is:{}".format(ip))
|
||||
scheme = 'http'
|
||||
@@ -272,7 +272,8 @@ class OpenStackDashboardTests(test_utils.OpenStackBaseTest,
|
||||
logging.info('Checking dashboard HAProxy settings...')
|
||||
unit = zaza_model.get_unit_from_name(
|
||||
zaza_model.get_lead_unit_name(self.application_name))
|
||||
logging.debug("... dashboard_ip is:{}".format(unit.public_address))
|
||||
logging.debug("... dashboard_ip is:{}".format(
|
||||
zaza_model.get_unit_public_address(unit)))
|
||||
conf = '/etc/haproxy/haproxy.cfg'
|
||||
port = '8888'
|
||||
set_alternate = {
|
||||
@@ -280,13 +281,18 @@ class OpenStackDashboardTests(test_utils.OpenStackBaseTest,
|
||||
}
|
||||
|
||||
request = urllib.request.Request(
|
||||
'http://{}:{}'.format(unit.public_address, port))
|
||||
'http://{}:{}'.format(
|
||||
zaza_model.get_unit_public_address(unit), port))
|
||||
|
||||
output = str(generic_utils.get_file_contents(unit, conf))
|
||||
|
||||
password = None
|
||||
for line in output.split('\n'):
|
||||
if "stats auth" in line:
|
||||
password = line.split(':')[1]
|
||||
break
|
||||
else:
|
||||
raise ValueError("'stats auth' not found in output'")
|
||||
base64string = base64.b64encode(
|
||||
bytes('{}:{}'.format('admin', password), 'ascii'))
|
||||
request.add_header(
|
||||
@@ -493,7 +499,8 @@ class OpenStackDashboardPolicydTests(policyd.BasePolicydSpecialization,
|
||||
"""
|
||||
unit = zaza_model.get_unit_from_name(
|
||||
zaza_model.get_lead_unit_name(self.application_name))
|
||||
logging.info("Dashboard is at {}".format(unit.public_address))
|
||||
logging.info("Dashboard is at {}".format(
|
||||
zaza_model.get_unit_public_address(unit)))
|
||||
overcloud_auth = openstack_utils.get_overcloud_auth()
|
||||
password = overcloud_auth['OS_PASSWORD']
|
||||
logging.info("admin password is {}".format(password))
|
||||
@@ -504,7 +511,7 @@ class OpenStackDashboardPolicydTests(policyd.BasePolicydSpecialization,
|
||||
self.get_horizon_url(), domain, username, password,
|
||||
cafile=self.cacert)
|
||||
# now attempt to get the domains page
|
||||
_url = self.url.format(unit.public_address)
|
||||
_url = self.url.format(zaza_model.get_unit_public_address(unit))
|
||||
logging.info("URL is {}".format(_url))
|
||||
result = client.get(_url)
|
||||
if result.status_code == 403:
|
||||
|
||||
@@ -26,8 +26,8 @@ import zaza.model
|
||||
import zaza.openstack.charm_tests.test_utils as test_utils
|
||||
import zaza.openstack.utilities.generic as generic_utils
|
||||
|
||||
from charmhelpers.core.host import CompareHostReleases
|
||||
from zaza.openstack.utilities.generic import get_series
|
||||
from zaza.openstack.utilities.os_versions import CompareHostReleases
|
||||
|
||||
from . import utils as rmq_utils
|
||||
from .utils import RmqNoMessageException
|
||||
@@ -49,6 +49,7 @@ class RmqTests(test_utils.OpenStackBaseTest):
|
||||
return '[{}-{}]'.format(uuid.uuid4(), time.time())
|
||||
|
||||
@tenacity.retry(
|
||||
reraise=True,
|
||||
retry=tenacity.retry_if_exception_type(RmqNoMessageException),
|
||||
wait=tenacity.wait_fixed(10),
|
||||
stop=tenacity.stop_after_attempt(2))
|
||||
@@ -57,6 +58,40 @@ class RmqTests(test_utils.OpenStackBaseTest):
|
||||
ssl=ssl,
|
||||
port=port)
|
||||
|
||||
def _search_for_message(self, amqp_msg, check_unit, ssl, port,
|
||||
amqp_msg_counter):
|
||||
"""Search for message in message queue.
|
||||
|
||||
WARNING: This will consume messages until it finds the target message.
|
||||
|
||||
:param amqp_msg: Message to search for
|
||||
:type amqp_msg: string
|
||||
:param check_unit: Unit to retrieve messages from
|
||||
:type check_unit: juju.unit.Unit
|
||||
:param ssl: Whether to use SSL when connecting to rabbit
|
||||
:type ssl: bool
|
||||
:param port: Port to use when connecting to rabbit
|
||||
:type port: Union[int, None]
|
||||
:param amqp_msg_counter: Number in test sequence of this message.
|
||||
:type amqp_msg: int
|
||||
:raises: RmqNoMessageException
|
||||
"""
|
||||
for i in range(100):
|
||||
amqp_msg_rcvd = self._retry_get_amqp_message(
|
||||
check_unit,
|
||||
ssl=ssl,
|
||||
port=port)
|
||||
if amqp_msg == amqp_msg_rcvd:
|
||||
logging.info(
|
||||
'Message {} received OK.'.format(amqp_msg_counter))
|
||||
break
|
||||
else:
|
||||
logging.info('Expected: {}'.format(amqp_msg))
|
||||
logging.info('Actual: {}'.format(amqp_msg_rcvd))
|
||||
else:
|
||||
msg = 'Message {} not found.'.format(amqp_msg_counter)
|
||||
raise RmqNoMessageException(msg)
|
||||
|
||||
def _test_rmq_amqp_messages_all_units(self, units,
|
||||
ssl=False, port=None):
|
||||
"""Reusable test to send/check amqp messages to every listed rmq unit.
|
||||
@@ -85,7 +120,7 @@ class RmqTests(test_utils.OpenStackBaseTest):
|
||||
|
||||
for dest_unit in units:
|
||||
dest_unit_name = dest_unit.entity_id
|
||||
dest_unit_host = dest_unit.public_address
|
||||
dest_unit_host = zaza.model.get_unit_public_address(dest_unit)
|
||||
dest_unit_host_name = host_names[dest_unit_name]
|
||||
|
||||
for check_unit in units:
|
||||
@@ -93,7 +128,8 @@ class RmqTests(test_utils.OpenStackBaseTest):
|
||||
if dest_unit_name == check_unit_name:
|
||||
logging.info("Skipping check for this unit to itself.")
|
||||
continue
|
||||
check_unit_host = check_unit.public_address
|
||||
check_unit_host = zaza.model.get_unit_public_address(
|
||||
check_unit)
|
||||
check_unit_host_name = host_names[check_unit_name]
|
||||
|
||||
amqp_msg_stamp = self._get_uuid_epoch_stamp()
|
||||
@@ -111,25 +147,22 @@ class RmqTests(test_utils.OpenStackBaseTest):
|
||||
port=port)
|
||||
|
||||
# Get amqp message
|
||||
logging.info('Get message from: {} '
|
||||
logging.info('Get messages from: {} '
|
||||
'({} {})'.format(check_unit_host,
|
||||
check_unit_name,
|
||||
check_unit_host_name))
|
||||
|
||||
amqp_msg_rcvd = self._retry_get_amqp_message(check_unit,
|
||||
ssl=ssl,
|
||||
port=port)
|
||||
|
||||
# Validate amqp message content
|
||||
if amqp_msg == amqp_msg_rcvd:
|
||||
logging.info('Message {} received '
|
||||
'OK.'.format(amqp_msg_counter))
|
||||
else:
|
||||
logging.error('Expected: {}'.format(amqp_msg))
|
||||
logging.error('Actual: {}'.format(amqp_msg_rcvd))
|
||||
msg = 'Message {} mismatch.'.format(amqp_msg_counter)
|
||||
try:
|
||||
self._search_for_message(
|
||||
amqp_msg,
|
||||
check_unit,
|
||||
ssl,
|
||||
port,
|
||||
amqp_msg_counter)
|
||||
except RmqNoMessageException:
|
||||
msg = 'Failed to retrieve message {}.'.format(
|
||||
amqp_msg_counter)
|
||||
raise Exception(msg)
|
||||
|
||||
amqp_msg_counter += 1
|
||||
|
||||
# Delete the test user
|
||||
|
||||
@@ -351,7 +351,7 @@ def configure_ssl_off(units, model_name=None, max_wait=60):
|
||||
|
||||
def is_ssl_enabled_on_unit(unit, port=None):
|
||||
"""Check a single juju rmq unit for ssl and port in the config file."""
|
||||
host = unit.public_address
|
||||
host = zaza.model.get_unit_public_address(unit)
|
||||
unit_name = unit.entity_id
|
||||
|
||||
conf_file = '/etc/rabbitmq/rabbitmq.conf'
|
||||
@@ -406,7 +406,7 @@ def connect_amqp_by_unit(unit, ssl=False,
|
||||
:param password: amqp user password
|
||||
:returns: pika amqp connection pointer or None if failed and non-fatal
|
||||
"""
|
||||
host = unit.public_address
|
||||
host = zaza.model.get_unit_public_address(unit)
|
||||
unit_name = unit.entity_id
|
||||
|
||||
if ssl:
|
||||
@@ -506,9 +506,9 @@ def get_amqp_message_by_unit(unit, queue="test",
|
||||
password=password)
|
||||
channel = connection.channel()
|
||||
method_frame, _, body = channel.basic_get(queue)
|
||||
body = body.decode()
|
||||
|
||||
if method_frame:
|
||||
body = body.decode()
|
||||
logging.debug('Retreived message from {} queue:\n{}'.format(queue,
|
||||
body))
|
||||
channel.basic_ack(method_frame.delivery_tag)
|
||||
|
||||
@@ -228,7 +228,7 @@ def keystone_federation_setup_idp1():
|
||||
"""Configure Keystone Federation for the local IdP #1."""
|
||||
test_saml_idp_unit = zaza.model.get_units("test-saml-idp1")[0]
|
||||
idp_remote_id = LOCAL_IDP_REMOTE_ID.format(
|
||||
test_saml_idp_unit.public_address)
|
||||
zaza.model.get_unit_public_address(test_saml_idp_unit))
|
||||
|
||||
keystone_federation_setup(
|
||||
federated_domain="federated_domain_idp1",
|
||||
@@ -241,7 +241,7 @@ def keystone_federation_setup_idp2():
|
||||
"""Configure Keystone Federation for the local IdP #2."""
|
||||
test_saml_idp_unit = zaza.model.get_units("test-saml-idp2")[0]
|
||||
idp_remote_id = LOCAL_IDP_REMOTE_ID.format(
|
||||
test_saml_idp_unit.public_address)
|
||||
zaza.model.get_unit_public_address(test_saml_idp_unit))
|
||||
|
||||
keystone_federation_setup(
|
||||
federated_domain="federated_domain_idp2",
|
||||
|
||||
@@ -54,7 +54,7 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
if self.vip:
|
||||
ip = self.vip
|
||||
else:
|
||||
ip = unit.public_address
|
||||
ip = zaza.model.get_unit_public_address(unit)
|
||||
|
||||
action = zaza.model.run_action(unit.entity_id, self.action)
|
||||
if "failed" in action.data["status"]:
|
||||
@@ -81,7 +81,7 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
keystone_ip = self.vip
|
||||
else:
|
||||
unit = zaza.model.get_units(self.application_name)[0]
|
||||
keystone_ip = unit.public_address
|
||||
keystone_ip = zaza.model.get_unit_public_address(unit)
|
||||
|
||||
horizon = "openstack-dashboard"
|
||||
horizon_vip = (zaza.model.get_application_config(horizon)
|
||||
@@ -90,7 +90,7 @@ class CharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
horizon_ip = horizon_vip
|
||||
else:
|
||||
unit = zaza.model.get_units("openstack-dashboard")[0]
|
||||
horizon_ip = unit.public_address
|
||||
horizon_ip = zaza.model.get_unit_public_address(unit)
|
||||
|
||||
if self.tls_rid:
|
||||
proto = "https"
|
||||
@@ -258,7 +258,7 @@ class BaseCharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
def test_run_get_sp_metadata_action(self):
|
||||
"""Validate the get-sp-metadata action."""
|
||||
unit = zaza.model.get_units(self.application_name)[0]
|
||||
ip = self.vip if self.vip else unit.public_address
|
||||
ip = self.vip if self.vip else zaza.model.get_unit_public_address(unit)
|
||||
|
||||
action = zaza.model.run_action(unit.entity_id, self.action)
|
||||
self.assertNotIn(
|
||||
@@ -283,14 +283,16 @@ class BaseCharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
def test_saml_mellon_redirects(self):
|
||||
"""Validate the horizon -> keystone -> IDP redirects."""
|
||||
unit = zaza.model.get_units(self.application_name)[0]
|
||||
keystone_ip = self.vip if self.vip else unit.public_address
|
||||
keystone_ip = self.vip if self.vip else (
|
||||
zaza.model.get_unit_public_address(unit))
|
||||
|
||||
horizon = "openstack-dashboard"
|
||||
horizon_config = zaza.model.get_application_config(horizon)
|
||||
horizon_vip = horizon_config.get("vip").get("value")
|
||||
unit = zaza.model.get_units("openstack-dashboard")[0]
|
||||
|
||||
horizon_ip = horizon_vip if horizon_vip else unit.public_address
|
||||
horizon_ip = horizon_vip if horizon_vip else (
|
||||
zaza.model.get_unit_public_address(unit))
|
||||
proto = "https" if self.tls_rid else "http"
|
||||
|
||||
# Use Keystone URL for < Focal
|
||||
@@ -299,8 +301,8 @@ class BaseCharmKeystoneSAMLMellonTest(BaseKeystoneTest):
|
||||
else:
|
||||
region = "default"
|
||||
|
||||
idp_address = zaza.model.get_units(
|
||||
self.test_saml_idp_app_name)[0].public_address
|
||||
idp_address = zaza.model.get_unit_public_address(
|
||||
zaza.model.get_units(self.test_saml_idp_app_name)[0])
|
||||
|
||||
horizon_url = "{}://{}/horizon/auth/login/".format(proto, horizon_ip)
|
||||
horizon_expect = '<option value="{0}">{1}</option>'.format(
|
||||
|
||||
@@ -73,7 +73,8 @@ class ParallelSeriesUpgradeTest(unittest.TestCase):
|
||||
# Set Feature Flag
|
||||
os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series"
|
||||
upgrade_groups = upgrade_utils.get_series_upgrade_groups(
|
||||
extra_filters=[_filter_etcd, _filter_easyrsa])
|
||||
extra_filters=[_filter_etcd, _filter_easyrsa],
|
||||
target_series=self.to_series)
|
||||
from_series = self.from_series
|
||||
to_series = self.to_series
|
||||
completed_machines = []
|
||||
|
||||
@@ -193,7 +193,8 @@ class ParallelSeriesUpgradeTest(unittest.TestCase):
|
||||
os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series"
|
||||
upgrade_groups = upgrade_utils.get_series_upgrade_groups(
|
||||
extra_filters=[upgrade_utils._filter_etcd,
|
||||
upgrade_utils._filter_easyrsa])
|
||||
upgrade_utils._filter_easyrsa],
|
||||
target_series=self.to_series)
|
||||
applications = model.get_status().applications
|
||||
completed_machines = []
|
||||
for group_name, group in upgrade_groups:
|
||||
|
||||
@@ -72,6 +72,12 @@ class SwiftImageCreateTest(test_utils.OpenStackBaseTest):
|
||||
class SwiftProxyTests(test_utils.OpenStackBaseTest):
|
||||
"""Tests specific to swift proxy."""
|
||||
|
||||
TEST_SEARCH_TARGET = 'd0'
|
||||
TEST_REMOVE_TARGET = 'd1'
|
||||
TEST_EXPECTED_RING_HOSTS = 1
|
||||
TEST_WEIGHT_TARGET = 999
|
||||
TEST_WEIGHT_INITIAL = 100
|
||||
|
||||
def test_901_pause_resume(self):
|
||||
"""Run pause and resume tests.
|
||||
|
||||
@@ -91,6 +97,59 @@ class SwiftProxyTests(test_utils.OpenStackBaseTest):
|
||||
action_params={})
|
||||
self.assertEqual(action.status, "completed")
|
||||
|
||||
def test_904_set_weight_action_and_validate_rebalance(self):
|
||||
"""Set weight of device in object ring."""
|
||||
logging.info('Running set-weight action on leader')
|
||||
action = zaza.model.run_action_on_leader(
|
||||
'swift-proxy',
|
||||
'set-weight',
|
||||
action_params={'ring': 'object',
|
||||
'search-value': self.TEST_SEARCH_TARGET,
|
||||
'weight': self.TEST_WEIGHT_TARGET})
|
||||
self.assertEqual(action.status, "completed")
|
||||
|
||||
logging.info('Validating builder updated as expected')
|
||||
result = swift_utils.search_builder('swift-proxy', 'object',
|
||||
self.TEST_SEARCH_TARGET)
|
||||
# disk weight is the 9th field of the second line and is a float
|
||||
disk_weight = int(result.split('\n')[1].split()[8].split('.')[0])
|
||||
self.assertEqual(disk_weight, self.TEST_WEIGHT_TARGET)
|
||||
self.assertTrue(swift_utils.is_proxy_ring_up_to_date('swift-proxy',
|
||||
'object'))
|
||||
|
||||
logging.info('Running set-weight on leader to reset weight back')
|
||||
action = zaza.model.run_action_on_leader(
|
||||
'swift-proxy',
|
||||
'set-weight',
|
||||
action_params={'ring': 'object',
|
||||
'search-value': self.TEST_SEARCH_TARGET,
|
||||
'weight': self.TEST_WEIGHT_INITIAL})
|
||||
self.assertEqual(action.status, "completed")
|
||||
self.assertTrue(
|
||||
swift_utils.is_ring_synced('swift-proxy', 'object',
|
||||
self.TEST_EXPECTED_RING_HOSTS))
|
||||
|
||||
def test_905_remove_device_action_and_validate_rebalance(self):
|
||||
"""Remove device from object ring."""
|
||||
logging.info('Running remove-devices action on leader')
|
||||
action = zaza.model.run_action_on_leader(
|
||||
'swift-proxy',
|
||||
'remove-devices',
|
||||
action_params={'ring': 'object',
|
||||
'search-value': self.TEST_REMOVE_TARGET})
|
||||
self.assertEqual(action.status, "completed")
|
||||
|
||||
logging.info('Validating builder updated as expected')
|
||||
result = swift_utils.search_builder('swift-proxy', 'object',
|
||||
self.TEST_REMOVE_TARGET)
|
||||
expected = 'No matching devices found'
|
||||
self.assertEqual(result.strip('\n'), expected)
|
||||
self.assertTrue(swift_utils.is_proxy_ring_up_to_date('swift-proxy',
|
||||
'object'))
|
||||
self.assertTrue(
|
||||
swift_utils.is_ring_synced('swift-proxy', 'object',
|
||||
self.TEST_EXPECTED_RING_HOSTS))
|
||||
|
||||
|
||||
class SwiftProxyMultiZoneTests(test_utils.OpenStackBaseTest):
|
||||
"""Tests specific to swift proxy in multi zone environment."""
|
||||
|
||||
@@ -1043,6 +1043,21 @@ class BaseDeferredRestartTest(BaseCharmTest):
|
||||
# clear status message.
|
||||
self.clear_hooks()
|
||||
|
||||
def get_service_timestamps(self, service):
|
||||
"""For units of self.application_name get start time of service.
|
||||
|
||||
:param service: Service to check, must be a systemd service
|
||||
:type service: str
|
||||
:returns: A dict timestamps keyed on unit name.
|
||||
:rtype: dict
|
||||
"""
|
||||
timestamps = {}
|
||||
for unit in model.get_units(self.application_name):
|
||||
timestamps[unit.entity_id] = model.get_systemd_service_active_time(
|
||||
unit.entity_id,
|
||||
service)
|
||||
return timestamps
|
||||
|
||||
def run_package_change_test(self, restart_package, restart_package_svc):
|
||||
"""Trigger a deferred restart by updating a package.
|
||||
|
||||
@@ -1055,8 +1070,32 @@ class BaseDeferredRestartTest(BaseCharmTest):
|
||||
after restart_package has changed.
|
||||
:type restart_package_service: str
|
||||
"""
|
||||
pre_timestamps = self.get_service_timestamps(
|
||||
restart_package_svc)
|
||||
self.trigger_deferred_restart_via_package(restart_package)
|
||||
|
||||
post_timestamps = self.get_service_timestamps(
|
||||
restart_package_svc)
|
||||
broken_units = []
|
||||
for unit_name in post_timestamps.keys():
|
||||
if pre_timestamps[unit_name] != post_timestamps[unit_name]:
|
||||
logging.error(
|
||||
"Service {} on unit {} should have start time of {} but"
|
||||
" it has {}".format(
|
||||
restart_package_svc,
|
||||
unit_name,
|
||||
pre_timestamps[unit_name],
|
||||
post_timestamps[unit_name]))
|
||||
broken_units.append(unit_name)
|
||||
if broken_units:
|
||||
msg = (
|
||||
"Units {} restarted service {} when disallowed by "
|
||||
"deferred_restarts").format(
|
||||
','.join(broken_units),
|
||||
restart_package_svc)
|
||||
raise Exception(msg)
|
||||
else:
|
||||
logging.info(
|
||||
"Service was {} not restarted.".format(restart_package_svc))
|
||||
self.check_show_deferred_restarts_wlm(restart_package_svc)
|
||||
self.check_show_deferred_events_action_restart(
|
||||
restart_package_svc,
|
||||
|
||||
@@ -85,10 +85,11 @@ import sys
|
||||
from zaza.openstack.utilities import (
|
||||
cli as cli_utils,
|
||||
generic as generic_utils,
|
||||
juju as juju_utils,
|
||||
openstack as openstack_utils,
|
||||
)
|
||||
|
||||
import zaza.utilities.juju as juju_utils
|
||||
|
||||
|
||||
def setup_sdn(network_config, keystone_session=None):
|
||||
"""Perform setup for Software Defined Network.
|
||||
|
||||
@@ -208,3 +208,15 @@ class CACERTNotFound(Exception):
|
||||
"""Could not find cacert."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LoadBalancerUnexpectedState(Exception):
|
||||
"""The LoadBalancer is in a unexpected state."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LoadBalancerUnrecoverableError(Exception):
|
||||
"""The LoadBalancer has reached to an unrecoverable error state."""
|
||||
|
||||
pass
|
||||
|
||||
@@ -622,7 +622,7 @@ def port_knock_units(units, port=22, expect_success=True):
|
||||
:returns: None if successful, Failure message otherwise
|
||||
"""
|
||||
for u in units:
|
||||
host = u.public_address
|
||||
host = model.get_unit_public_address(u)
|
||||
connected = is_port_open(port, host)
|
||||
if not connected and expect_success:
|
||||
return 'Socket connect failed.'
|
||||
@@ -723,3 +723,22 @@ def get_leaders_and_non_leaders(application_name):
|
||||
else:
|
||||
non_leaders.append(unit)
|
||||
return leader, non_leaders
|
||||
|
||||
|
||||
def add_loop_device(unit, size=10):
|
||||
"""Add a loopback device to a Juju unit.
|
||||
|
||||
:param unit: The unit name on which to create the device.
|
||||
:type unit: str
|
||||
|
||||
:param size: The size in GB of the device.
|
||||
:type size: int
|
||||
|
||||
:returns: The device name.
|
||||
"""
|
||||
loop_name = '/home/ubuntu/loop.img'
|
||||
truncate = 'truncate --size {}GB {}'.format(size, loop_name)
|
||||
losetup = 'losetup --find {}'.format(loop_name)
|
||||
lofind = 'losetup -a | grep {} | cut -f1 -d ":"'.format(loop_name)
|
||||
cmd = "sudo sh -c '{} && {} && {}'".format(truncate, losetup, lofind)
|
||||
return model.run_on_unit(unit, cmd)
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
import logging
|
||||
import functools
|
||||
import subprocess
|
||||
|
||||
import zaza.model
|
||||
import zaza.utilities.juju
|
||||
|
||||
|
||||
@@ -312,67 +310,3 @@ def get_subordinate_units(unit_list, charm_name=None, status=None,
|
||||
charm_name=charm_name,
|
||||
status=status,
|
||||
model_name=model_name)
|
||||
|
||||
|
||||
def add_storage(unit, label, pool, size):
|
||||
"""Add storage to a Juju unit.
|
||||
|
||||
:param unit: The unit name (i.e: ceph-osd/0)
|
||||
:type unit: str
|
||||
|
||||
:param label: The storage label (i.e: osd-devices)
|
||||
:type label: str
|
||||
|
||||
:param pool: The pool on which to allocate the storage (i.e: cinder)
|
||||
:type pool: str
|
||||
|
||||
:size: The size in GB of the storage to attach.
|
||||
:type size: int
|
||||
|
||||
:returns: The name of the allocated storage.
|
||||
"""
|
||||
rv = subprocess.check_output(['juju', 'add-storage', unit,
|
||||
'{}={},{}'.format(label, pool,
|
||||
str(size) + 'GB')],
|
||||
stderr=subprocess.STDOUT)
|
||||
return rv.decode('UTF-8').replace('added storage ', '').split(' ')[0]
|
||||
|
||||
|
||||
def detach_storage(storage_name):
|
||||
"""Detach previously allocated Juju storage."""
|
||||
subprocess.check_call(['juju', 'detach-storage', storage_name])
|
||||
|
||||
|
||||
def remove_storage(storage_name, force=False):
|
||||
"""Remove Juju storage.
|
||||
|
||||
:param storage_name: The name of the previously allocated Juju storage.
|
||||
:type storage_name: str
|
||||
|
||||
:param force: If False (default), require that the storage be detached
|
||||
before it can be removed.
|
||||
:type force: bool
|
||||
"""
|
||||
cmd = ['juju', 'remove-storage', storage_name]
|
||||
if force:
|
||||
cmd.append('--force')
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
|
||||
def add_loop_device(unit, size=10):
|
||||
"""Add a loopback device to a Juju unit.
|
||||
|
||||
:param unit: The unit name on which to create the device.
|
||||
:type unit: str
|
||||
|
||||
:param size: The size in GB of the device.
|
||||
:type size: int
|
||||
|
||||
:returns: The device name.
|
||||
"""
|
||||
loop_name = '/home/ubuntu/loop.img'
|
||||
truncate = 'truncate --size {}GB {}'.format(size, loop_name)
|
||||
losetup = 'losetup --find {}'.format(loop_name)
|
||||
lofind = 'losetup -a | grep {} | cut -f1 -d ":"'.format(loop_name)
|
||||
cmd = "sudo sh -c '{} && {} && {}'".format(truncate, losetup, lofind)
|
||||
return zaza.model.run_on_unit(unit, cmd)
|
||||
|
||||
@@ -1554,7 +1554,7 @@ def create_bgp_peer(neutron_client, peer_application_name='quagga',
|
||||
:rtype: dict
|
||||
"""
|
||||
peer_unit = model.get_units(peer_application_name)[0]
|
||||
peer_ip = peer_unit.public_address
|
||||
peer_ip = model.get_unit_public_address(peer_unit)
|
||||
bgp_peers = neutron_client.list_bgp_peers(name=peer_application_name)
|
||||
if len(bgp_peers['bgp_peers']) == 0:
|
||||
logging.info('Creating BGP Peer')
|
||||
@@ -2019,6 +2019,9 @@ def get_undercloud_auth():
|
||||
def get_keystone_ip(model_name=None):
|
||||
"""Return the IP address to use when communicating with keystone api.
|
||||
|
||||
If there are multiple VIP addresses specified in the 'vip' option for the
|
||||
keystone unit, then ONLY the first one is returned.
|
||||
|
||||
:param model_name: Name of model to query.
|
||||
:type model_name: str
|
||||
:returns: IP address
|
||||
@@ -2029,9 +2032,10 @@ def get_keystone_ip(model_name=None):
|
||||
'vip',
|
||||
model_name=model_name)
|
||||
if vip_option:
|
||||
return vip_option
|
||||
# strip the option, splits on whitespace and return the first one.
|
||||
return vip_option.strip().split()[0]
|
||||
unit = model.get_units('keystone', model_name=model_name)[0]
|
||||
return unit.public_address
|
||||
return model.get_unit_public_address(unit)
|
||||
|
||||
|
||||
def get_keystone_api_version(model_name=None):
|
||||
@@ -2225,6 +2229,22 @@ def get_images_by_name(glance, image_name):
|
||||
return [i for i in glance.images.list() if image_name == i.name]
|
||||
|
||||
|
||||
def get_volumes_by_name(cinder, volume_name):
|
||||
"""Get all cinder volume objects with the given name.
|
||||
|
||||
:param cinder: Authenticated cinderclient
|
||||
:type cinder: cinderclient.Client
|
||||
:param image_name: Name of volume
|
||||
:type image_name: str
|
||||
:returns: List of cinder volumes
|
||||
:rtype: List[cinderclient.v3.volume, ...]
|
||||
"""
|
||||
return [i for i in cinder.volumes.list() if volume_name == i.name]
|
||||
|
||||
|
||||
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60),
|
||||
reraise=True,
|
||||
retry=tenacity.retry_if_exception_type(urllib.error.URLError))
|
||||
def find_cirros_image(arch):
|
||||
"""Return the url for the latest cirros image for the given architecture.
|
||||
|
||||
@@ -2251,7 +2271,7 @@ def download_image(image_url, target_file):
|
||||
|
||||
:param image_url: URL to download image from
|
||||
:type image_url: str
|
||||
:param target_file: Local file to savee image to
|
||||
:param target_file: Local file to save image to
|
||||
:type target_file: str
|
||||
"""
|
||||
opener = get_urllib_opener()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
This module contains a number of functions for upgrading OpenStack.
|
||||
"""
|
||||
import logging
|
||||
import zaza.openstack.utilities.juju as juju_utils
|
||||
import zaza.utilities.juju as juju_utils
|
||||
|
||||
import zaza.model
|
||||
from zaza import sync_wrapper
|
||||
|
||||
@@ -278,3 +278,105 @@ PACKAGE_CODENAMES = {
|
||||
('4', 'victoria'),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
UBUNTU_RELEASES = (
|
||||
'lucid',
|
||||
'maverick',
|
||||
'natty',
|
||||
'oneiric',
|
||||
'precise',
|
||||
'quantal',
|
||||
'raring',
|
||||
'saucy',
|
||||
'trusty',
|
||||
'utopic',
|
||||
'vivid',
|
||||
'wily',
|
||||
'xenial',
|
||||
'yakkety',
|
||||
'zesty',
|
||||
'artful',
|
||||
'bionic',
|
||||
'cosmic',
|
||||
'disco',
|
||||
'eoan',
|
||||
'focal',
|
||||
'groovy',
|
||||
'hirsute',
|
||||
'impish',
|
||||
)
|
||||
|
||||
|
||||
class BasicStringComparator(object):
|
||||
"""Provides a class that will compare strings from an iterator type object.
|
||||
|
||||
Used to provide > and < comparisons on strings that may not necessarily be
|
||||
alphanumerically ordered. e.g. OpenStack or Ubuntu releases AFTER the
|
||||
z-wrap.
|
||||
"""
|
||||
|
||||
_list = None
|
||||
|
||||
def __init__(self, item):
|
||||
"""Do init."""
|
||||
if self._list is None:
|
||||
raise Exception("Must define the _list in the class definition!")
|
||||
try:
|
||||
self.index = self._list.index(item)
|
||||
except Exception:
|
||||
raise KeyError("Item '{}' is not in list '{}'"
|
||||
.format(item, self._list))
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Do equals."""
|
||||
assert isinstance(other, str) or isinstance(other, self.__class__)
|
||||
return self.index == self._list.index(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Do not equals."""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
"""Do less than."""
|
||||
assert isinstance(other, str) or isinstance(other, self.__class__)
|
||||
return self.index < self._list.index(other)
|
||||
|
||||
def __ge__(self, other):
|
||||
"""Do greater than or equal."""
|
||||
return not self.__lt__(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
"""Do greater than."""
|
||||
assert isinstance(other, str) or isinstance(other, self.__class__)
|
||||
return self.index > self._list.index(other)
|
||||
|
||||
def __le__(self, other):
|
||||
"""Do less than or equals."""
|
||||
return not self.__gt__(other)
|
||||
|
||||
def __str__(self):
|
||||
"""Give back the item at the index.
|
||||
|
||||
This is so it can be used in comparisons like:
|
||||
|
||||
s_mitaka = CompareOpenStack('mitaka')
|
||||
s_newton = CompareOpenstack('newton')
|
||||
|
||||
assert s_newton > s_mitaka
|
||||
|
||||
:returns: <string>
|
||||
"""
|
||||
return self._list[self.index]
|
||||
|
||||
|
||||
class CompareHostReleases(BasicStringComparator):
|
||||
"""Provide comparisons of Ubuntu releases.
|
||||
|
||||
Use in the form of
|
||||
|
||||
if CompareHostReleases(release) > 'trusty':
|
||||
# do something with mitaka
|
||||
"""
|
||||
|
||||
_list = UBUNTU_RELEASES
|
||||
|
||||
@@ -207,7 +207,7 @@ def get_swift_storage_topology(model_name=None):
|
||||
region = app_config['storage-region']['value']
|
||||
zone = app_config['zone']['value']
|
||||
for unit in zaza.model.get_units(app_name, model_name=model_name):
|
||||
topology[unit.public_address] = {
|
||||
topology[zaza.model.get_unit_public_address(unit)] = {
|
||||
'app_name': app_name,
|
||||
'unit': unit,
|
||||
'region': region,
|
||||
@@ -298,3 +298,69 @@ def create_object(swift_client, proxy_app, storage_topology, resource_prefix,
|
||||
storage_topology,
|
||||
model_name=model_name)
|
||||
return container_name, object_name, obj_replicas
|
||||
|
||||
|
||||
def search_builder(proxy_app, ring, search_target, model_name=None):
|
||||
"""Run a swift-ring-builder search.
|
||||
|
||||
:param proxy_app: Name of proxy application
|
||||
:type proxy_app: str
|
||||
:param ring: Name of ring (one of: object, account, container)
|
||||
:type ring: str
|
||||
:param search_target: device search string (see: man swift-ring-builder)
|
||||
:type search_target: str
|
||||
:param model_name: Model to point environment at
|
||||
:type model_name: str
|
||||
:returns: stdout - full stdout output from swift-ring-builder cmd
|
||||
:rtype: str
|
||||
"""
|
||||
cmd = ('swift-ring-builder /etc/swift/{}.builder search {}'
|
||||
''.format(ring, search_target))
|
||||
result = zaza.model.run_on_leader(proxy_app, cmd,
|
||||
model_name=model_name)
|
||||
return result['Stdout']
|
||||
|
||||
|
||||
def is_proxy_ring_up_to_date(proxy_app, ring, model_name=None):
|
||||
"""Check if the ring file is up-to-date with changes of the builder.
|
||||
|
||||
:param proxy_app: Name of proxy application
|
||||
:type proxy_app: str
|
||||
:param ring: Name of ring (one of: object, account, container)
|
||||
:type ring: str
|
||||
:param model_name: Model to point environment at
|
||||
:type model_name: str
|
||||
:returns: True if swift-ring-builder denotes ring.gz file is up-to-date
|
||||
:rtype: str
|
||||
"""
|
||||
logging.info('Checking ring file matches builder file')
|
||||
cmd = ('swift-ring-builder /etc/swift/{}.builder | '
|
||||
'grep "Ring file .* is"'.format(ring))
|
||||
result = zaza.model.run_on_leader(proxy_app, cmd, model_name=model_name)
|
||||
expected = ('Ring file /etc/swift/{}.ring.gz is up-to-date'
|
||||
''.format(ring))
|
||||
return bool(result['Stdout'].strip('\n') == expected)
|
||||
|
||||
|
||||
def is_ring_synced(proxy_app, ring, expected_hosts, model_name=None):
|
||||
"""Check if md5sums of rings on swift-storage are synced to this proxy.
|
||||
|
||||
:param proxy_app: Name of proxy application
|
||||
:type proxy_app: str
|
||||
:param ring: Name of ring (one of: object, account, container)
|
||||
:type ring: str
|
||||
:param expoected_hosts: Number of swift-storage hosts in test environment
|
||||
:type search_target: int
|
||||
:param model_name: Model to point environment at
|
||||
:type model_name: str
|
||||
:returns: True if all expected_hosts matched md5sum of proxy ring file
|
||||
:rtype: bool
|
||||
"""
|
||||
logging.info('Checking ring md5sums on storage unit(s) against proxy')
|
||||
zaza.model.block_until_all_units_idle()
|
||||
cmd = ('swift-recon {} --md5 | '
|
||||
'grep -A1 "ring md5" | tail -1'.format(ring))
|
||||
result = zaza.model.run_on_leader(proxy_app, cmd, model_name=model_name)
|
||||
expected = ('{num}/{num} hosts matched, 0 error[s] while checking hosts.'
|
||||
''.format(num=expected_hosts))
|
||||
return bool(result['Stdout'].strip('\n') == expected)
|
||||
|
||||
@@ -24,6 +24,7 @@ from zaza.openstack.utilities.os_versions import (
|
||||
OPENSTACK_CODENAMES,
|
||||
UBUNTU_OPENSTACK_RELEASE,
|
||||
OPENSTACK_RELEASES_PAIRS,
|
||||
CompareHostReleases,
|
||||
)
|
||||
|
||||
"""
|
||||
@@ -48,7 +49,11 @@ SERVICE_GROUPS = (
|
||||
'nova-compute', 'ceph-osd',
|
||||
'swift-proxy', 'swift-storage']))
|
||||
|
||||
UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster']
|
||||
UPGRADE_EXCLUDE_LIST = [
|
||||
'rabbitmq-server',
|
||||
'percona-cluster',
|
||||
'glance-simplestreams-sync',
|
||||
]
|
||||
|
||||
|
||||
def get_upgrade_candidates(model_name=None, filters=None):
|
||||
@@ -96,6 +101,25 @@ def _filter_openstack_upgrade_list(app, app_config, model_name=None):
|
||||
return False
|
||||
|
||||
|
||||
def _make_filter_percona_cluster_at(target_series):
|
||||
def _filter_percona_cluster(app, app_config, model_name=None):
|
||||
charm_name = extract_charm_name_from_url(app_config['charm'])
|
||||
if charm_name == "percona-cluster":
|
||||
logging.warning(
|
||||
"Excluding percona-cluster from upgrade, "
|
||||
"as no candidate in %s", target_series)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _noop_filter(*args, **kwargs):
|
||||
return False
|
||||
|
||||
if target_series and CompareHostReleases(target_series) >= "focal":
|
||||
return _filter_percona_cluster
|
||||
|
||||
return _noop_filter
|
||||
|
||||
|
||||
def _filter_non_openstack_services(app, app_config, model_name=None):
|
||||
charm_options = zaza.model.get_application_config(
|
||||
app, model_name=model_name).keys()
|
||||
@@ -168,7 +192,8 @@ def get_upgrade_groups(model_name=None, extra_filters=None):
|
||||
return _build_service_groups(apps_in_model)
|
||||
|
||||
|
||||
def get_series_upgrade_groups(model_name=None, extra_filters=None):
|
||||
def get_series_upgrade_groups(model_name=None, extra_filters=None,
|
||||
target_series=None):
|
||||
"""Place apps in the model into their upgrade groups.
|
||||
|
||||
Place apps in the model into their upgrade groups. If an app is deployed
|
||||
@@ -176,10 +201,17 @@ def get_series_upgrade_groups(model_name=None, extra_filters=None):
|
||||
|
||||
:param model_name: Name of model to query.
|
||||
:type model_name: str
|
||||
:param extra_filters: filters to apply to the upgrade groups
|
||||
:type extra_filters: Callable
|
||||
:param target_series: The series that will be series upgraded to.
|
||||
:type target_series: Optional[str]
|
||||
:returns: List of tuples(group name, applications)
|
||||
:rtype: List[Tuple[str, Dict[str, ANY]]]
|
||||
"""
|
||||
filters = [_filter_subordinates]
|
||||
filters = [
|
||||
_filter_subordinates,
|
||||
_make_filter_percona_cluster_at(target_series),
|
||||
]
|
||||
filters = _apply_extra_filters(filters, extra_filters)
|
||||
apps_in_model = get_upgrade_candidates(
|
||||
model_name=model_name,
|
||||
|
||||
Reference in New Issue
Block a user