# Copyright (c) 2015 Red Hat, Inc. # # 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 uuid from manilaclient.openstack.common.apiclient import exceptions as manila_ex import mock import testtools from sahara import exceptions from sahara.service import shares from sahara.tests.unit import base _NAMENODE_IPS = ['192.168.122.3', '192.168.122.4'] _DATANODE_IPS = ['192.168.122.5', '192.168.122.6', '192.168.122.7'] class _FakeShare(object): def __init__(self, id='12345678-1234-1234-1234-123456789012', share_proto='NFS', export_location='192.168.122.1:/path', access_list=None): self.id = id self.share_proto = share_proto self.export_location = export_location self.allow = mock.Mock() self.deny = mock.Mock() self.access_list = mock.Mock(return_value=access_list or []) def _mock_node_group(ips, share_list): # Returns a mocked node group and a list of mocked # execute_command functions for its instances. execute_mocks = [mock.Mock(return_value=(None, "centos")) for ip in ips] get_id = mock.Mock(return_value=uuid.uuid4()) instances = [ mock.Mock( internal_ip=ip, remote=mock.Mock( return_value=mock.Mock( __enter__=mock.Mock( return_value=mock.Mock( execute_command=execute_mocks[index])), __exit__=mock.Mock()))) for index, ip in enumerate(ips)] node_group = mock.Mock(instances=instances, shares=share_list, __getitem__=get_id) return node_group, execute_mocks def _setup_calls(): return [ mock.call('lsb_release -is'), mock.call('rpm -q nfs-utils || yum install -y nfs-utils', run_as_root=True)] def _expected_calls(local_path, remote_path, access_argument): return [ mock.call('mkdir -p %s' % local_path, run_as_root=True), mock.call("mount | grep '%(remote_path)s' | grep '%(local_path)s' | " "grep nfs || mount -t nfs %(access_argument)s " "%(remote_path)s %(local_path)s" % { "local_path": local_path, "remote_path": remote_path, "access_argument": access_argument }, run_as_root=True)] class TestShares(base.SaharaTestCase): @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_mount_nfs_shares_to_ng(self, f_manilaclient, f_context): share = _FakeShare() f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock(return_value=share))) namenode_group, namenode_executors = _mock_node_group( _NAMENODE_IPS, [{ 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'rw', 'path': '/mnt/localpath' }]) datanode_group, datanode_executors = _mock_node_group( _DATANODE_IPS, []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[]) shares.mount_shares(cluster) permissions = [mock.call('ip', ip, 'rw') for ip in _NAMENODE_IPS] share.allow.assert_has_calls(permissions, any_order=True) for executor in namenode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/localpath', '192.168.122.1:/path', '-w')) for executor in datanode_executors: self.assertEqual(0, executor.call_count) @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_mount_nfs_shares_to_cluster(self, f_manilaclient, f_context): global_share = _FakeShare() namenode_only_share = _FakeShare( id='DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF', export_location='192.168.122.2:/path') all_shares = {share.id: share for share in (global_share, namenode_only_share)} f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock( side_effect=lambda x: all_shares[x]))) namenode_group, namenode_executors = _mock_node_group( ['192.168.122.3', '192.168.122.4'], [ { 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'rw', 'path': '/mnt/localpath' }, { 'id': 'DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF' } ]) datanode_group, datanode_executors = _mock_node_group( ['192.168.122.5', '192.168.122.6', '192.168.122.7'], []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[ { 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'ro', 'path': '/mnt/somanylocalpaths' } ]) shares.mount_shares(cluster) all_permissions = [mock.call('ip', ip, 'ro') for ip in _NAMENODE_IPS + _DATANODE_IPS] global_share.allow.assert_has_calls(all_permissions, any_order=True) namenode_permissions = [mock.call('ip', ip, 'rw') for ip in _NAMENODE_IPS] namenode_only_share.allow.assert_has_calls(namenode_permissions, any_order=True) for executor in namenode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/somanylocalpaths', '192.168.122.1:/path', '-r') + _expected_calls('/mnt/DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF', '192.168.122.2:/path', '-w'), any_order=True) self.assertEqual(6, executor.call_count) for executor in datanode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/somanylocalpaths', '192.168.122.1:/path', '-r')) self.assertEqual(4, executor.call_count) @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_share_does_not_exist(self, f_manilaclient, f_context): f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock( side_effect=manila_ex.NotFound))) namenode_group, namenode_executors = _mock_node_group( ['192.168.122.3', '192.168.122.4'], [ { 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'rw', 'path': '/mnt/localpath' }, { 'id': 'DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF' } ]) datanode_group, datanode_executors = _mock_node_group( ['192.168.122.5', '192.168.122.6', '192.168.122.7'], []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[ { 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'ro', 'path': '/mnt/somanylocalpaths' } ]) with testtools.ExpectedException(exceptions.NotFoundException): shares.mount_shares(cluster) @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_acl_exists_unexpected_type(self, f_manilaclient, f_context): share = _FakeShare(access_list=[mock.Mock( access_level='wat', access_to=ip, access_type='ip') for ip in _NAMENODE_IPS]) f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock(return_value=share))) namenode_group, namenode_executors = _mock_node_group( _NAMENODE_IPS, [{ 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'rw', 'path': '/mnt/localpath' }]) datanode_group, datanode_executors = _mock_node_group( _DATANODE_IPS, []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[]) shares.mount_shares(cluster) self.assertEqual(0, share.allow.call_count) for executor in namenode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/localpath', '192.168.122.1:/path', '-w')) for executor in datanode_executors: self.assertEqual(0, executor.call_count) @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_acl_exists_no_recreate(self, f_manilaclient, f_context): share = _FakeShare(access_list=[mock.Mock( access_level='rw', access_to=ip, access_type='ip') for ip in _NAMENODE_IPS]) f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock(return_value=share))) namenode_group, namenode_executors = _mock_node_group( _NAMENODE_IPS, [{ 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'ro', 'path': '/mnt/localpath' }]) datanode_group, datanode_executors = _mock_node_group( _DATANODE_IPS, []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[]) shares.mount_shares(cluster) self.assertEqual(0, share.allow.call_count) for executor in namenode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/localpath', '192.168.122.1:/path', '-r')) for executor in datanode_executors: self.assertEqual(0, executor.call_count) @mock.patch('sahara.context.set_current_instance_id') @mock.patch('sahara.utils.openstack.manila.client') def test_acl_exists_recreate(self, f_manilaclient, f_context): share = _FakeShare(access_list=[mock.Mock( access_level='ro', access_to=ip, access_type='ip', id="access_id") for ip in _NAMENODE_IPS]) f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock(return_value=share))) namenode_group, namenode_executors = _mock_node_group( _NAMENODE_IPS, [{ 'id': '12345678-1234-1234-1234-123456789012', 'access_level': 'rw', 'path': '/mnt/localpath' }]) datanode_group, datanode_executors = _mock_node_group( _DATANODE_IPS, []) cluster = mock.Mock( node_groups=[namenode_group, datanode_group], shares=[]) shares.mount_shares(cluster) namenode_denials = [mock.call('access_id') for ip in _NAMENODE_IPS] share.deny.assert_has_calls(namenode_denials) namenode_permissions = [mock.call('ip', ip, 'rw') for ip in _NAMENODE_IPS] share.allow.assert_has_calls(namenode_permissions, any_order=True) for executor in namenode_executors: executor.assert_has_calls( _setup_calls() + _expected_calls('/mnt/localpath', '192.168.122.1:/path', '-w')) for executor in datanode_executors: self.assertEqual(0, executor.call_count) def test_get_share_path(self): share_list = [ {'id': 'the_share_id', 'path': '/mnt/mymountpoint'}, {'id': 'the_share_id', 'path': '/mnt/othermountpoint'}, {'id': '123456', 'path': '/mnt/themountpoint'} ] url = 'manila://the_share_id/the_path' path = shares.get_share_path(url, share_list) self.assertEqual("/mnt/mymountpoint/the_path", path) share_list.pop(0) path = shares.get_share_path(url, share_list) self.assertEqual("/mnt/othermountpoint/the_path", path) share_list.pop(0) path = shares.get_share_path(url, share_list) self.assertIsNone(path) @mock.patch('sahara.utils.openstack.manila.client') def test_get_share_path_default(self, f_manilaclient): share_list = [ {'id': 'i_have_no_mnt'} ] share = _FakeShare(share_list[0]['id']) f_manilaclient.return_value = mock.Mock( shares=mock.Mock( get=mock.Mock(return_value=share))) url = 'manila://i_have_no_mnt/the_path' path = shares.get_share_path(url, share_list) self.assertEqual("/mnt/i_have_no_mnt/the_path", path)