charm-ceph-radosgw/unit_tests/test_ceph.py
James Page 314aaffeb9 Add support for cephx pool grouping and permissions
Sync charmhelpers and add configuration option to allow access
to ceph pools to be limited based on grouping.

The radosgw will only require access to pools in the 'objects'
group - which probably won't be shared with other services,
but does ensure that compromise on a radosgw unit does not
allow access to volumes and instances on the same underlying
ceph backend.

Added creation of .rgw.root pool to broker requests to ensure
that all pools are added to the 'objects' group, avoiding the
need for rw permissionson the radosgw.gateway key.

Change-Id: I88953b44e9994c1d0235f617faaeb892b7abed9c
Partial-Bug: 1424771
2017-02-14 18:08:13 +00:00

382 lines
15 KiB
Python

# Copyright 2016 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.
import sys
from mock import patch, call, MagicMock
# python-apt is not installed as part of test-requirements but is imported by
# some charmhelpers modules so create a fake import.
mock_apt = MagicMock()
mock_apt.apt_pkg = MagicMock()
sys.modules['apt'] = mock_apt
sys.modules['apt_pkg'] = mock_apt.apt_pkg
import ceph # noqa
import utils # noqa
from test_utils import CharmTestCase # noqa
TO_PATCH = [
'config',
'get_unit_hostname',
'os',
'subprocess',
'time',
]
class CephRadosGWCephTests(CharmTestCase):
def setUp(self):
super(CephRadosGWCephTests, self).setUp(ceph, TO_PATCH)
self.config.side_effect = self.test_config.get
def test_is_quorum_leader(self):
self.os.path.exists.return_value = True
self.get_unit_hostname.return_value = 'myhost'
self.subprocess.check_output.return_value = '{"state": "leader"}'
self.assertEqual(ceph.is_quorum(), True)
def test_is_quorum_notleader(self):
self.os.path.exists.return_value = True
self.get_unit_hostname.return_value = 'myhost'
self.subprocess.check_output.return_value = '{"state": "notleader"}'
self.assertEqual(ceph.is_quorum(), False)
def test_is_quorum_valerror(self):
self.os.path.exists.return_value = True
self.get_unit_hostname.return_value = 'myhost'
self.subprocess.check_output.return_value = "'state': 'bob'}"
self.assertEqual(ceph.is_quorum(), False)
def test_is_quorum_no_asok(self):
self.os.path.exists.return_value = False
self.assertEqual(ceph.is_quorum(), False)
def test_is_leader(self):
self.get_unit_hostname.return_value = 'myhost'
self.os.path.exists.return_value = True
self.subprocess.check_output.return_value = '{"state": "leader"}'
self.assertEqual(ceph.is_leader(), True)
def test_is_leader_notleader(self):
self.get_unit_hostname.return_value = 'myhost'
self.os.path.exists.return_value = True
self.subprocess.check_output.return_value = '{"state": "notleader"}'
self.assertEqual(ceph.is_leader(), False)
def test_is_leader_valerror(self):
self.get_unit_hostname.return_value = 'myhost'
self.os.path.exists.return_value = True
self.subprocess.check_output.return_value = "'state': 'bob'}"
self.assertEqual(ceph.is_leader(), False)
def test_is_leader_noasok(self):
self.get_unit_hostname.return_value = 'myhost'
self.os.path.exists.return_value = False
self.assertEqual(ceph.is_leader(), False)
def test_wait_for_quorum_yes(self):
results = [True, False]
def quorum():
return results.pop()
_is_quorum = self.patch('is_quorum')
_is_quorum.side_effect = quorum
ceph.wait_for_quorum()
self.time.sleep.assert_called_with(3)
def test_wait_for_quorum_no(self):
_is_quorum = self.patch('is_quorum')
_is_quorum.return_value = True
ceph.wait_for_quorum()
self.assertFalse(self.time.sleep.called)
def test_wait_for_bootstrap(self):
results = [True, False]
def bootstrapped():
return results.pop()
_is_bootstrapped = self.patch('is_bootstrapped')
_is_bootstrapped.side_effect = bootstrapped
ceph.wait_for_bootstrap()
self.time.sleep.assert_called_with(3)
def test_add_bootstrap_hint(self):
self.get_unit_hostname.return_value = 'myhost'
cmd = [
"ceph",
"--admin-daemon",
'/var/run/ceph/ceph-mon.myhost.asok',
"add_bootstrap_peer_hint",
'mypeer'
]
self.os.path.exists.return_value = True
ceph.add_bootstrap_hint('mypeer')
self.subprocess.call.assert_called_with(cmd)
def test_add_bootstrap_hint_noasok(self):
self.get_unit_hostname.return_value = 'myhost'
self.os.path.exists.return_value = False
ceph.add_bootstrap_hint('mypeer')
self.assertFalse(self.subprocess.call.called)
def test_is_osd_disk(self):
# XXX Insert real sgdisk output
self.subprocess.check_output.return_value = \
'Partition GUID code: 4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D'
self.assertEqual(ceph.is_osd_disk('/dev/fmd0'), True)
def test_is_osd_disk_no(self):
# XXX Insert real sgdisk output
self.subprocess.check_output.return_value = \
'Partition GUID code: 5FBD7E29-9D25-41B8-AFD0-062C0CEFF05D'
self.assertEqual(ceph.is_osd_disk('/dev/fmd0'), False)
def test_rescan_osd_devices(self):
cmd = [
'udevadm', 'trigger',
'--subsystem-match=block', '--action=add'
]
ceph.rescan_osd_devices()
self.subprocess.call.assert_called_with(cmd)
def test_zap_disk(self):
cmd = [
'sgdisk', '--zap-all', '/dev/fmd0',
]
ceph.zap_disk('/dev/fmd0')
self.subprocess.check_call.assert_called_with(cmd)
def test_import_osd_bootstrap_key(self):
self.os.path.exists.return_value = False
cmd = [
'ceph-authtool',
'/var/lib/ceph/bootstrap-osd/ceph.keyring',
'--create-keyring',
'--name=client.bootstrap-osd',
'--add-key=mykey',
]
ceph.import_osd_bootstrap_key('mykey')
self.subprocess.check_call.assert_called_with(cmd)
def test_is_bootstrapped(self):
self.os.path.exists.return_value = True
self.assertEqual(ceph.is_bootstrapped(), True)
self.os.path.exists.return_value = False
self.assertEqual(ceph.is_bootstrapped(), False)
def test_import_radosgw_key(self):
self.os.path.exists.return_value = False
ceph.import_radosgw_key('mykey')
cmd = [
'ceph-authtool',
'/etc/ceph/keyring.rados.gateway',
'--create-keyring',
'--name=client.radosgw.gateway',
'--add-key=mykey'
]
self.subprocess.check_call.assert_called_with(cmd)
def test_get_named_key_create(self):
self.get_unit_hostname.return_value = "myhost"
self.subprocess.check_output.return_value = """
[client.dummy]
key = AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ==
"""
self.assertEqual(ceph.get_named_key('dummy'),
'AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ==')
cmd = [
'ceph',
'--name', 'mon.',
'--keyring',
'/var/lib/ceph/mon/ceph-myhost/keyring',
'auth', 'get-or-create', 'client.dummy',
'mon', 'allow r', 'osd', 'allow rwx'
]
self.subprocess.check_output.assert_called_with(cmd)
def test_get_named_key_get(self):
self.get_unit_hostname.return_value = "myhost"
key = "AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ=="
self.subprocess.check_output.return_value = key
self.assertEqual(ceph.get_named_key('dummy'), key)
cmd = [
'ceph',
'--name', 'mon.',
'--keyring',
'/var/lib/ceph/mon/ceph-myhost/keyring',
'auth', 'get-or-create', 'client.dummy',
'mon', 'allow r', 'osd', 'allow rwx'
]
self.subprocess.check_output.assert_called_with(cmd)
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
'.add_op_create_pool')
def test_create_rgw_pools_rq_with_prefix(self, mock_broker):
self.test_config.set('rgw-lightweight-pool-pg-num', 10)
self.test_config.set('ceph-osd-replication-count', 3)
self.test_config.set('rgw-buckets-pool-weight', 19)
ceph.get_create_rgw_pools_rq(prefix='us-east')
mock_broker.assert_has_calls([
call(replica_count=3, weight=19, name='us-east.rgw.buckets',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw.root',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw.control',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw.gc',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw.buckets.index',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.rgw.buckets.extra',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.log',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.intent-log',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.usage',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.users',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.users.email',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.users.swift',
group='objects'),
call(pg_num=10, replica_count=3, name='us-east.users.uid',
group='objects'),
call(pg_num=10, replica_count=3, name='.rgw.root',
group='objects')]
)
@patch.object(mock_apt.apt_pkg, 'version_compare', lambda *args: -1)
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
'.add_op_create_pool')
def test_create_rgw_pools_rq_no_prefix_pre_jewel(self, mock_broker):
self.test_config.set('rgw-lightweight-pool-pg-num', -1)
self.test_config.set('ceph-osd-replication-count', 3)
self.test_config.set('rgw-buckets-pool-weight', 19)
ceph.get_create_rgw_pools_rq(prefix=None)
mock_broker.assert_has_calls([
call(weight=19, replica_count=3, name='.rgw.buckets',
group='objects'),
call(weight=0.10, replica_count=3, name='.rgw',
group='objects'),
call(weight=0.10, replica_count=3, name='.rgw.root',
group='objects'),
call(weight=0.10, replica_count=3, name='.rgw.control',
group='objects'),
call(weight=0.10, replica_count=3, name='.rgw.gc',
group='objects'),
call(weight=1.00, replica_count=3, name='.rgw.buckets.index',
group='objects'),
call(weight=1.00, replica_count=3, name='.rgw.buckets.extra',
group='objects'),
call(weight=0.10, replica_count=3, name='.log',
group='objects'),
call(weight=0.10, replica_count=3, name='.intent-log',
group='objects'),
call(weight=0.10, replica_count=3, name='.usage',
group='objects'),
call(weight=0.10, replica_count=3, name='.users',
group='objects'),
call(weight=0.10, replica_count=3, name='.users.email',
group='objects'),
call(weight=0.10, replica_count=3, name='.users.swift',
group='objects'),
call(weight=0.10, replica_count=3, name='.users.uid',
group='objects')]
)
@patch.object(mock_apt.apt_pkg, 'version_compare', lambda *args: 0)
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
'.add_op_request_access_to_group')
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
'.add_op_create_pool')
def test_create_rgw_pools_rq_no_prefix_post_jewel(self, mock_broker,
mock_request_access):
self.test_config.set('rgw-lightweight-pool-pg-num', -1)
self.test_config.set('ceph-osd-replication-count', 3)
self.test_config.set('rgw-buckets-pool-weight', 19)
self.test_config.set('restrict-ceph-pools', True)
ceph.get_create_rgw_pools_rq(prefix=None)
mock_broker.assert_has_calls([
call(weight=19, replica_count=3, name='default.rgw.buckets',
group='objects'),
call(weight=0.10, replica_count=3, name='default.rgw',
group='objects'),
call(weight=0.10, replica_count=3, name='default.rgw.root',
group='objects'),
call(weight=0.10, replica_count=3, name='default.rgw.control',
group='objects'),
call(weight=0.10, replica_count=3, name='default.rgw.gc',
group='objects'),
call(weight=1.00, replica_count=3,
name='default.rgw.buckets.index',
group='objects'),
call(weight=1.00, replica_count=3,
name='default.rgw.buckets.extra',
group='objects'),
call(weight=0.10, replica_count=3, name='default.log',
group='objects'),
call(weight=0.10, replica_count=3, name='default.intent-log',
group='objects'),
call(weight=0.10, replica_count=3, name='default.usage',
group='objects'),
call(weight=0.10, replica_count=3, name='default.users',
group='objects'),
call(weight=0.10, replica_count=3, name='default.users.email',
group='objects'),
call(weight=0.10, replica_count=3, name='default.users.swift',
group='objects'),
call(weight=0.10, replica_count=3, name='default.users.uid',
group='objects'),
call(weight=0.10, replica_count=3, name='.rgw.root',
group='objects')]
)
mock_request_access.assert_called_with(key_name='radosgw.gateway',
name='objects',
permission='rwx')
@patch.object(mock_apt.apt_pkg, 'version_compare', lambda *args: -1)
@patch.object(utils, 'lsb_release',
lambda: {'DISTRIB_CODENAME': 'trusty'})
@patch.object(utils, 'add_source')
@patch.object(utils, 'apt_update')
@patch.object(utils, 'apt_install')
def test_setup_ipv6_install_backports(self, mock_add_source,
mock_apt_update,
mock_apt_install):
utils.setup_ipv6()
self.assertTrue(mock_apt_update.called)
self.assertTrue(mock_apt_install.called)
@patch.object(mock_apt.apt_pkg, 'version_compare', lambda *args: 0)
@patch.object(utils, 'lsb_release',
lambda: {'DISTRIB_CODENAME': 'trusty'})
@patch.object(utils, 'add_source')
@patch.object(utils, 'apt_update')
@patch.object(utils, 'apt_install')
def test_setup_ipv6_not_install_backports(self, mock_add_source,
mock_apt_update,
mock_apt_install):
utils.setup_ipv6()
self.assertFalse(mock_apt_update.called)
self.assertFalse(mock_apt_install.called)