Pre-focal-enablement for rabbitmq-server charm

This patch updates the rabbitmq charm to work on focal as part of other
test bundles, but does not update the charm for testing on focal. Hence
focal/ussuri bundles are not added and the metadata.yaml does NOT
include focal in this patchset.

A further patchset will be submitted that adds the focal testing to the
charm.  In particular, this patchset is required for the cinder focal
enablement.

Change-Id: Ia239b7c2f0ed2383e220cf0fa4ade443149a3b32
This commit is contained in:
Alex Kavanagh 2020-03-19 16:18:43 +00:00
parent 8ddb700817
commit 5f6b1830d0
4 changed files with 182 additions and 19 deletions

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import re
import sys
@ -177,12 +178,20 @@ def list_vhosts():
Returns a list of all the available vhosts
"""
try:
output = subprocess.check_output([RABBITMQ_CTL, 'list_vhosts'])
cmd = [RABBITMQ_CTL, 'list_vhosts']
# NOTE(ajkavanagh): In focal and above, rabbitmq-server now has a
# --formatter option.
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
cmd.append('--formatter=json')
output = subprocess.check_output(cmd)
output = output.decode('utf-8')
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
decoded = json.loads(output)
return [l['name'] for l in decoded]
# NOTE(jamespage): Earlier rabbitmqctl versions append "...done"
# to the output of list_vhosts
if '...done' in output:
elif '...done' in output:
return output.split('\n')[1:-2]
else:
return output.split('\n')[1:-1]
@ -199,12 +208,22 @@ def vhost_queue_info(vhost):
"""
cmd = [RABBITMQ_CTL, '-p', vhost, 'list_queues',
'name', 'messages', 'consumers']
# NOTE(ajkavanagh): In focal and above, rabbitmq-server now has a
# --formatter option.
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
cmd.append('--formatter=json')
output = subprocess.check_output(cmd).decode('utf-8')
queue_info = []
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
decoded = json.loads(output)
# note that the json is already in the desired output of queue_info
# below
return decoded
# NOTE(jamespage): Earlier rabbitmqctl versions append "...done"
# to the output of list_queues
if '...done' in output:
elif '...done' in output:
queues = output.split('\n')[1:-2]
else:
queues = output.split('\n')[1:-1]
@ -233,8 +252,20 @@ def create_vhost(vhost):
def user_exists(user):
cmd = [RABBITMQ_CTL, 'list_users']
# NOTE(ajkavanagh): In focal and above, rabbitmq-server now has a
# --formatter option.
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
cmd.append('--formatter=json')
out = subprocess.check_output(cmd).decode('utf-8')
decoded = json.loads(out)
users = [l['user'] for l in decoded]
return user in users
# NOTE(ajkavanagh): pre 3.8.2 the code needs to deal with just a text
# output
out = subprocess.check_output(cmd).decode('utf-8')
for line in out.split('\n')[1:]:
lines = out.split('\n')[1:]
for line in lines:
_user = line.split('\t')[0]
if _user == user:
return True
@ -314,7 +345,7 @@ def set_ha_mode(vhost, mode, params=None, sync_mode='automatic'):
"all, exactly, nodes"))
log("Setting HA policy to vhost '%s'" % vhost, level='INFO')
set_policy(vhost, 'HA', '^(?!amq\.).*', value)
set_policy(vhost, 'HA', r'^(?!amq\.).*', value)
def clear_ha_mode(vhost, name='HA', force=False):
@ -430,7 +461,7 @@ def wait_app():
try:
status_cmd = ['rabbitmqctl', 'status']
log(subprocess.check_output(status_cmd).decode('utf-8'), DEBUG)
except:
except Exception:
pass
raise ex
@ -582,7 +613,7 @@ def leave_cluster():
rabbitmqctl('reset')
start_app()
log('Successfully left cluster gracefully.')
except:
except Exception:
# error, no nodes available for clustering
log('Cannot leave cluster, we might be the last disc-node in the '
'cluster.', level=ERROR)
@ -770,9 +801,19 @@ def services():
@cached
def nodes(get_running=False):
''' Get list of nodes registered in the RabbitMQ cluster '''
# NOTE(ajkavanagh): In focal and above, rabbitmq-server now has a
# --formatter option.
if caching_cmp_pkgrevno('rabbitmq-server', '3.8.2') >= 0:
cmd = [RABBITMQ_CTL, 'cluster_status', '--formatter=json']
output = subprocess.check_output(cmd).decode('utf-8')
decoded = json.loads(output)
if get_running:
return decoded['running_nodes']
return decoded['disk_nodes'] + decoded['ram_nodes']
out = rabbitmqctl_normalized_output('cluster_status')
cluster_status = {}
for m in re.finditer("{([^,]+),(?!\[{)\[([^\]]*)", out):
for m in re.finditer(r"{([^,]+),(?!\[{)\[([^\]]*)", out):
state = m.group(1)
items = m.group(2).split(',')
items = [x.replace("'", '').strip() for x in items]
@ -813,7 +854,7 @@ def get_node_hostname(ip_addr):
''' Resolve IP address to hostname '''
try:
nodename = get_hostname(ip_addr, fqdn=False)
except:
except Exception:
log('Cannot resolve hostname for %s using DNS servers' % ip_addr,
level=WARNING)
log('Falling back to use socket.gethostname()',

View File

@ -108,5 +108,5 @@ commands =
functest-run-suite --keep-model --bundle {posargs}
[flake8]
ignore = E402,E226
ignore = E402,E226,E504
exclude = */charmhelpers

View File

@ -92,6 +92,18 @@ RABBITMQCTL_CLUSTERSTATUS_RUNNING = b"""Cluster status of node 'rabbit@juju-deve
{partitions,[]}]
"""
RABBITMQCTL_CLUSTERSTATUS_RUNNING_382 = b"""
{"running_nodes": [
"rabbit@juju-devel3-machine-14",
"rabbit@juju-devel3-machine-19"],
"disk_nodes":
["rabbit@juju-devel3-machine-14",
"rabbit@juju-devel3-machine-19"],
"ram_nodes": ["rabbit@juju-devel3-machine-42"]
}
"""
RABBITMQCTL_CLUSTERSTATUS_SOLO = b"""Cluster status of node 'rabbit@juju-devel3-machine-14' ...
[{nodes,[{disc,['rabbit@juju-devel3-machine-14']}]},
{running_nodes,['rabbit@juju-devel3-machine-14']},
@ -99,6 +111,17 @@ RABBITMQCTL_CLUSTERSTATUS_SOLO = b"""Cluster status of node 'rabbit@juju-devel3-
{partitions,[]}]
"""
RABBITMQCTL_CLUSTERSTATUS_SOLO_382 = b"""
{"running_nodes": [
"rabbit@juju-devel3-machine-14"],
"disk_nodes":
["rabbit@juju-devel3-machine-14"],
"ram_nodes": []
}
"""
RABBITMQCTL_LIST_QUEUES = b"""Listing queues ...
a_sample_queue 0 1
cinder-scheduler.cinder 0 1
@ -107,6 +130,12 @@ myqueue 0 1
...done
"""
RABBITMQCTL_LIST_QUEUES_382 = (
b'[{"name": "a_sample_queue", "messages": 0, "consumers": 1},'
b'{"name": "cinder-scheduler.cinder", "messages": 0, "consumers": 1},'
b'{"name": "cinder-fanout-12345", "messages": 250, "consumers": 0},'
b'{"name": "myqueue", "messages": 0, "consumers": 1}]')
RABBITMQCTL_LIST_VHOSTS = b"""Listing vhosts ...
/
landscape
@ -114,6 +143,9 @@ openstack
...done
"""
RABBITMQCTL_LIST_VHOSTS_382 = (b'[{"name": "/"},{"name": "landscape"},'
b'{"name": "openstack"}]')
class UtilsTests(CharmTestCase):
def setUp(self):
@ -189,38 +221,81 @@ class UtilsTests(CharmTestCase):
mock_running_nodes.return_value = ['a', 'b']
self.assertTrue(rabbit_utils.clustered())
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_nodes(self, mock_subprocess):
def test_nodes(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a clustered deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_RUNNING
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.nodes(),
['rabbit@juju-devel3-machine-14',
'rabbit@juju-devel3-machine-19',
'rabbit@juju-devel3-machine-42'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_running_nodes(self, mock_subprocess):
def test_nodes_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a clustered deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_RUNNING_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.nodes(),
['rabbit@juju-devel3-machine-14',
'rabbit@juju-devel3-machine-19',
'rabbit@juju-devel3-machine-42'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_running_nodes(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a clustered deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_RUNNING
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.running_nodes(),
['rabbit@juju-devel3-machine-14',
'rabbit@juju-devel3-machine-19'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_list_vhosts(self, mock_subprocess):
def test_running_nodes_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a clustered deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_RUNNING_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.running_nodes(),
['rabbit@juju-devel3-machine-14',
'rabbit@juju-devel3-machine-19'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_list_vhosts(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure list_vhosts parses output into the proper list'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_LIST_VHOSTS
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.list_vhosts(),
['/', 'landscape', 'openstack'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_vhost_queue_info(self, mock_subprocess):
def test_list_vhosts_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure list_vhosts parses output into the proper list for
rabbitmq_server 3.8.2+
'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_LIST_VHOSTS_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.list_vhosts(),
['/', 'landscape', 'openstack'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_vhost_queue_info(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure vhost_queue_info parses output into the proper format/info'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_LIST_QUEUES
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.vhost_queue_info('openstack'),
[{'name': 'a_sample_queue', 'messages': 0,
'consumers': 1},
@ -230,19 +305,59 @@ class UtilsTests(CharmTestCase):
'consumers': 0},
{'name': 'myqueue', 'messages': 0, 'consumers': 1}])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_nodes_solo(self, mock_subprocess):
def test_vhost_queue_info_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure vhost_queue_info parses output into the proper format/info'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_LIST_QUEUES_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.vhost_queue_info('openstack'),
[{'name': 'a_sample_queue', 'messages': 0,
'consumers': 1},
{'name': 'cinder-scheduler.cinder', 'messages': 0,
'consumers': 1},
{'name': 'cinder-fanout-12345', 'messages': 250,
'consumers': 0},
{'name': 'myqueue', 'messages': 0, 'consumers': 1}])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_nodes_solo(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a single unit deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_SOLO
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.nodes(),
['rabbit@juju-devel3-machine-14'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_running_nodes_solo(self, mock_subprocess):
def test_nodes_solo_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a single unit deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_SOLO_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.nodes(),
['rabbit@juju-devel3-machine-14'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_running_nodes_solo(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a single unit deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_SOLO
mock_cmp_pkgrevno.return_value = -1
self.assertEqual(rabbit_utils.running_nodes(),
['rabbit@juju-devel3-machine-14'])
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.subprocess')
def test_running_nodes_solo_382(self, mock_subprocess, mock_cmp_pkgrevno):
'''Ensure cluster_status can be parsed for a single unit deployment'''
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_SOLO_382
mock_cmp_pkgrevno.return_value = 0
self.assertEqual(rabbit_utils.running_nodes(),
['rabbit@juju-devel3-machine-14'])
@ -702,6 +817,7 @@ class UtilsTests(CharmTestCase):
rabbit_utils.forget_cluster_node('a')
mock_rabbitmqctl.assert_called_with('forget_cluster_node', 'a')
@mock.patch('rabbit_utils.caching_cmp_pkgrevno')
@mock.patch('rabbit_utils.forget_cluster_node')
@mock.patch('rabbit_utils.relations_for_id')
@mock.patch('rabbit_utils.subprocess')
@ -709,10 +825,12 @@ class UtilsTests(CharmTestCase):
def test_check_cluster_memberships(self, mock_relation_ids,
mock_subprocess,
mock_relations_for_id,
mock_forget_cluster_node):
mock_forget_cluster_node,
mock_cmp_pkgrevno):
mock_relation_ids.return_value = [0]
mock_subprocess.check_output.return_value = \
RABBITMQCTL_CLUSTERSTATUS_RUNNING
mock_cmp_pkgrevno.return_value = -1
mock_relations_for_id.return_value = [
{'clustered': 'juju-devel3-machine-14'},
{'clustered': 'juju-devel3-machine-19'},

View File

@ -306,6 +306,7 @@ class RelationUtil(CharmTestCase):
if os.path.exists(tmpdir):
shutil.rmtree(tmpdir)
@patch('rabbit_utils.create_user')
@patch('rabbitmq_server_relations.local_unit')
@patch('charmhelpers.contrib.charmsupport.nrpe.NRPE.add_check')
@patch('subprocess.check_call')
@ -318,14 +319,17 @@ class RelationUtil(CharmTestCase):
@patch('rabbitmq_server_relations.charm_dir')
@patch('subprocess.check_output')
@patch('rabbitmq_server_relations.config')
def test_update_nrpe_checks(self, mock_config, mock_check_output,
def test_update_nrpe_checks(self,
mock_config,
mock_check_output,
mock_charm_dir, mock_fchown,
mock_get_nagios_hostname,
mock_get_nagios_unit_name, mock_config2,
mock_nrpe_relation_ids,
mock_get_rabbit_password_on_disk,
mock_check_call, mock_add_check,
mock_local_unit):
mock_local_unit,
mock_create_user):
self.test_config.set('ssl', 'on')