Merge "[NetApp] Add manage/unmanage of share servers"
This commit is contained in:
commit
a69f5333c4
|
@ -1757,6 +1757,15 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
|||
}
|
||||
self.send_request('volume-rename', api_args)
|
||||
|
||||
@na_utils.trace
|
||||
def rename_vserver(self, vserver_name, new_vserver_name):
|
||||
"""Rename a vserver."""
|
||||
api_args = {
|
||||
'vserver-name': vserver_name,
|
||||
'new-name': new_vserver_name,
|
||||
}
|
||||
self.send_request('vserver-rename', api_args)
|
||||
|
||||
@na_utils.trace
|
||||
def modify_volume(self, aggregate_name, volume_name,
|
||||
thin_provisioned=False, snapshot_policy=None,
|
||||
|
|
|
@ -83,6 +83,22 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
|
|||
def unmanage_snapshot(self, snapshot):
|
||||
raise NotImplementedError
|
||||
|
||||
def manage_existing_with_server(
|
||||
self, share, driver_options, share_server=None):
|
||||
return self.library.manage_existing(
|
||||
share, driver_options, share_server=share_server)
|
||||
|
||||
def unmanage_with_server(self, share, share_server=None):
|
||||
self.library.unmanage(share, share_server=share_server)
|
||||
|
||||
def manage_existing_snapshot_with_server(
|
||||
self, snapshot, driver_options, share_server=None):
|
||||
return self.library.manage_existing_snapshot(
|
||||
snapshot, driver_options, share_server=share_server)
|
||||
|
||||
def unmanage_snapshot_with_server(self, snapshot, share_server=None):
|
||||
self.library.unmanage_snapshot(snapshot, share_server=share_server)
|
||||
|
||||
def update_access(self, context, share, access_rules, add_rules,
|
||||
delete_rules, **kwargs):
|
||||
self.library.update_access(context, share, access_rules, add_rules,
|
||||
|
@ -240,3 +256,15 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
|
|||
|
||||
def ensure_shares(self, context, shares):
|
||||
return self.library.ensure_shares(context, shares)
|
||||
|
||||
def get_share_server_network_info(
|
||||
self, context, share_server, identifier, driver_options):
|
||||
return self.library.get_share_server_network_info(
|
||||
context, share_server, identifier, driver_options)
|
||||
|
||||
def manage_server(self, context, share_server, identifier, driver_options):
|
||||
return self.library.manage_server(
|
||||
context, share_server, identifier, driver_options)
|
||||
|
||||
def unmanage_server(self, server_details, security_services=None):
|
||||
return self.library.unmanage_server(server_details, security_services)
|
||||
|
|
|
@ -83,6 +83,20 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
|
|||
def unmanage_snapshot(self, snapshot):
|
||||
self.library.unmanage_snapshot(snapshot)
|
||||
|
||||
def manage_existing_with_server(
|
||||
self, share, driver_options, share_server=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def unmanage_with_server(self, share, share_server=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def manage_existing_snapshot_with_server(
|
||||
self, snapshot, driver_options, share_server=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def unmanage_snapshot_with_server(self, snapshot, share_server=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def update_access(self, context, share, access_rules, add_rules,
|
||||
delete_rules, **kwargs):
|
||||
self.library.update_access(context, share, access_rules, add_rules,
|
||||
|
@ -256,3 +270,13 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
|
|||
|
||||
def ensure_shares(self, context, shares):
|
||||
return self.library.ensure_shares(context, shares)
|
||||
|
||||
def get_share_server_network_info(
|
||||
self, context, share_server, identifier, driver_options):
|
||||
raise NotImplementedError
|
||||
|
||||
def manage_server(self, context, share_server, identifier, driver_options):
|
||||
raise NotImplementedError
|
||||
|
||||
def unmanage_server(self, server_details, security_services=None):
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -997,15 +997,15 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||
raise exception.ShareSnapshotIsBusy(snapshot_name=snapshot_name)
|
||||
|
||||
@na_utils.trace
|
||||
def manage_existing(self, share, driver_options):
|
||||
vserver, vserver_client = self._get_vserver(share_server=None)
|
||||
def manage_existing(self, share, driver_options, share_server=None):
|
||||
vserver, vserver_client = self._get_vserver(share_server=share_server)
|
||||
share_size = self._manage_container(share, vserver, vserver_client)
|
||||
export_locations = self._create_export(share, None, vserver,
|
||||
export_locations = self._create_export(share, share_server, vserver,
|
||||
vserver_client)
|
||||
return {'size': share_size, 'export_locations': export_locations}
|
||||
|
||||
@na_utils.trace
|
||||
def unmanage(self, share):
|
||||
def unmanage(self, share, share_server=None):
|
||||
pass
|
||||
|
||||
@na_utils.trace
|
||||
|
@ -1110,9 +1110,10 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||
raise exception.ManageInvalidShare(reason=msg % msg_args)
|
||||
|
||||
@na_utils.trace
|
||||
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||
def manage_existing_snapshot(
|
||||
self, snapshot, driver_options, share_server=None):
|
||||
"""Brings an existing snapshot under Manila management."""
|
||||
vserver, vserver_client = self._get_vserver(share_server=None)
|
||||
vserver, vserver_client = self._get_vserver(share_server=share_server)
|
||||
share_name = self._get_backend_share_name(snapshot['share_id'])
|
||||
existing_snapshot_name = snapshot.get('provider_location')
|
||||
new_snapshot_name = self._get_backend_snapshot_name(snapshot['id'])
|
||||
|
@ -1159,7 +1160,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||
return {'size': size, 'provider_location': new_snapshot_name}
|
||||
|
||||
@na_utils.trace
|
||||
def unmanage_snapshot(self, snapshot):
|
||||
def unmanage_snapshot(self, snapshot, share_server=None):
|
||||
"""Removes the specified snapshot from Manila management."""
|
||||
|
||||
@na_utils.trace
|
||||
|
|
|
@ -67,21 +67,23 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||
check_for_setup_error())
|
||||
|
||||
@na_utils.trace
|
||||
def _get_vserver(self, share_server=None):
|
||||
def _get_vserver(self, share_server=None, vserver_name=None):
|
||||
|
||||
if not share_server:
|
||||
if share_server:
|
||||
backend_details = share_server.get('backend_details')
|
||||
vserver = backend_details.get(
|
||||
'vserver_name') if backend_details else None
|
||||
|
||||
if not vserver:
|
||||
msg = _('Vserver name is absent in backend details. Please '
|
||||
'check whether Vserver was created properly.')
|
||||
raise exception.VserverNotSpecified(msg)
|
||||
elif vserver_name:
|
||||
vserver = vserver_name
|
||||
else:
|
||||
msg = _('Share server not provided')
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
backend_details = share_server.get('backend_details')
|
||||
vserver = backend_details.get(
|
||||
'vserver_name') if backend_details else None
|
||||
|
||||
if not vserver:
|
||||
msg = _('Vserver name is absent in backend details. Please '
|
||||
'check whether Vserver was created properly.')
|
||||
raise exception.VserverNotSpecified(msg)
|
||||
|
||||
if not self._client.vserver_exists(vserver):
|
||||
raise exception.VserverNotFound(vserver=vserver)
|
||||
|
||||
|
@ -401,3 +403,37 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
|
|||
if options['ipv6-enabled']:
|
||||
versions.append(6)
|
||||
return versions
|
||||
|
||||
def manage_server(self, context, share_server, identifier, driver_options):
|
||||
"""Manages a vserver by renaming it and returning backend_details."""
|
||||
new_vserver_name = self._get_vserver_name(share_server['id'])
|
||||
old_vserver_name = self._get_correct_vserver_old_name(identifier)
|
||||
|
||||
if new_vserver_name != old_vserver_name:
|
||||
self._client.rename_vserver(old_vserver_name, new_vserver_name)
|
||||
|
||||
backend_details = {'vserver_name': new_vserver_name}
|
||||
return new_vserver_name, backend_details
|
||||
|
||||
def unmanage_server(self, server_details, security_services=None):
|
||||
pass
|
||||
|
||||
def get_share_server_network_info(
|
||||
self, context, share_server, identifier, driver_options):
|
||||
"""Returns a list of IPs for each vserver network interface."""
|
||||
vserver_name = self._get_correct_vserver_old_name(identifier)
|
||||
|
||||
vserver, vserver_client = self._get_vserver(vserver_name=vserver_name)
|
||||
|
||||
interfaces = vserver_client.get_network_interfaces()
|
||||
allocations = []
|
||||
for lif in interfaces:
|
||||
allocations.append(lif['address'])
|
||||
return allocations
|
||||
|
||||
def _get_correct_vserver_old_name(self, identifier):
|
||||
|
||||
# In case vserver_name includes the template, we check and add it here
|
||||
if not self._client.vserver_exists(identifier):
|
||||
return self._get_vserver_name(identifier)
|
||||
return identifier
|
||||
|
|
|
@ -146,7 +146,10 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
|
|||
def _get_export_location(share):
|
||||
"""Returns IP address and export location of an NFS share."""
|
||||
export_location = share['export_location'] or ':'
|
||||
return export_location.rsplit(':', 1)
|
||||
result = export_location.rsplit(':', 1)
|
||||
if len(result) != 2:
|
||||
return ['', '']
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _get_temp_export_policy_name():
|
||||
|
|
|
@ -3067,6 +3067,20 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
|||
self.client.send_request.assert_called_once_with(
|
||||
'volume-rename', volume_rename_api_args)
|
||||
|
||||
def test_rename_vserver(self):
|
||||
|
||||
vserver_api_args = {
|
||||
'vserver-name': fake.VSERVER_NAME,
|
||||
'new-name': fake.VSERVER_NAME_2,
|
||||
}
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
||||
self.client.rename_vserver(fake.VSERVER_NAME, fake.VSERVER_NAME_2)
|
||||
|
||||
self.client.send_request.assert_called_once_with(
|
||||
'vserver-rename', vserver_api_args
|
||||
)
|
||||
|
||||
def test_modify_volume_no_optional_args(self):
|
||||
|
||||
self.mock_object(self.client, 'send_request')
|
||||
|
|
|
@ -1534,13 +1534,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
self.assertFalse(vserver_client.split_volume_clone.called)
|
||||
self.assertFalse(vserver_client.soft_delete_snapshot.called)
|
||||
|
||||
def test_manage_existing(self):
|
||||
@ddt.data(None, fake.VSERVER1)
|
||||
def test_manage_existing(self, fake_vserver):
|
||||
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
vserver_client)))
|
||||
mock__get_vserver = self.mock_object(
|
||||
self.library, '_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1, vserver_client)))
|
||||
mock_manage_container = self.mock_object(
|
||||
self.library,
|
||||
'_manage_container',
|
||||
|
@ -1550,24 +1550,29 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
'_create_export',
|
||||
mock.Mock(return_value=fake.NFS_EXPORTS))
|
||||
|
||||
result = self.library.manage_existing(fake.SHARE, {})
|
||||
result = self.library.manage_existing(fake.SHARE, {},
|
||||
share_server=fake_vserver)
|
||||
|
||||
expected = {
|
||||
'size': fake.SHARE_SIZE,
|
||||
'export_locations': fake.NFS_EXPORTS
|
||||
}
|
||||
|
||||
mock__get_vserver.assert_called_once_with(share_server=fake_vserver)
|
||||
mock_manage_container.assert_called_once_with(fake.SHARE,
|
||||
fake.VSERVER1,
|
||||
vserver_client)
|
||||
|
||||
mock_create_export.assert_called_once_with(fake.SHARE,
|
||||
None,
|
||||
fake_vserver,
|
||||
fake.VSERVER1,
|
||||
vserver_client)
|
||||
self.assertDictEqual(expected, result)
|
||||
|
||||
def test_unmanage(self):
|
||||
@ddt.data(None, fake.VSERVER1)
|
||||
def test_unmanage(self, fake_vserver):
|
||||
|
||||
result = self.library.unmanage(fake.SHARE)
|
||||
result = self.library.unmanage(fake.SHARE, share_server=fake_vserver)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
@ -1782,7 +1787,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
fake.FLEXVOL_TO_MANAGE,
|
||||
vserver_client)
|
||||
|
||||
def test_manage_existing_snapshot(self):
|
||||
@ddt.data(None, fake.VSERVER1)
|
||||
def test_manage_existing_snapshot(self, fake_vserver):
|
||||
|
||||
vserver_client = mock.Mock()
|
||||
mock_get_vserver = self.mock_object(
|
||||
|
@ -1791,13 +1797,13 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
vserver_client.get_volume.return_value = fake.FLEXVOL_TO_MANAGE
|
||||
vserver_client.volume_has_snapmirror_relationships.return_value = False
|
||||
result = self.library.manage_existing_snapshot(
|
||||
fake.SNAPSHOT_TO_MANAGE, {})
|
||||
fake.SNAPSHOT_TO_MANAGE, {}, share_server=fake_vserver)
|
||||
|
||||
share_name = self.library._get_backend_share_name(
|
||||
fake.SNAPSHOT['share_id'])
|
||||
new_snapshot_name = self.library._get_backend_snapshot_name(
|
||||
fake.SNAPSHOT['id'])
|
||||
mock_get_vserver.assert_called_once_with(share_server=None)
|
||||
mock_get_vserver.assert_called_once_with(share_server=fake_vserver)
|
||||
(vserver_client.volume_has_snapmirror_relationships.
|
||||
assert_called_once_with(fake.FLEXVOL_TO_MANAGE))
|
||||
vserver_client.rename_snapshot.assert_called_once_with(
|
||||
|
@ -1870,9 +1876,10 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
self.library.manage_existing_snapshot,
|
||||
fake.SNAPSHOT_TO_MANAGE, {})
|
||||
|
||||
def test_unmanage_snapshot(self):
|
||||
@ddt.data(None, fake.VSERVER1)
|
||||
def test_unmanage_snapshot(self, fake_vserver):
|
||||
|
||||
result = self.library.unmanage_snapshot(fake.SNAPSHOT)
|
||||
result = self.library.unmanage_snapshot(fake.SNAPSHOT, fake_vserver)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
|
|
@ -110,6 +110,25 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
self.assertRaises(exception.InvalidInput,
|
||||
self.library._get_vserver)
|
||||
|
||||
def test_get_vserver_no_share_server_with_vserver_name(self):
|
||||
fake_vserver_client = 'fake_client'
|
||||
|
||||
mock_vserver_exists = self.mock_object(
|
||||
self.library._client, 'vserver_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_api_client',
|
||||
mock.Mock(return_value=fake_vserver_client))
|
||||
|
||||
result_vserver, result_vserver_client = self.library._get_vserver(
|
||||
share_server=None, vserver_name=fake.VSERVER1)
|
||||
|
||||
mock_vserver_exists.assert_called_once_with(
|
||||
fake.VSERVER1
|
||||
)
|
||||
self.assertEqual(fake.VSERVER1, result_vserver)
|
||||
self.assertEqual(fake_vserver_client, result_vserver_client)
|
||||
|
||||
def test_get_vserver_no_backend_details(self):
|
||||
|
||||
fake_share_server = copy.deepcopy(fake.SHARE_SERVER)
|
||||
|
@ -186,6 +205,69 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||
}
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@ddt.data('fake', fake.IDENTIFIER)
|
||||
def test_manage_server(self, fake_vserver_name):
|
||||
|
||||
self.mock_object(context,
|
||||
'get_admin_context',
|
||||
mock.Mock(return_value='fake_admin_context'))
|
||||
mock_get_vserver_name = self.mock_object(
|
||||
self.library, '_get_vserver_name',
|
||||
mock.Mock(return_value=fake_vserver_name))
|
||||
|
||||
new_identifier, new_details = self.library.manage_server(
|
||||
context, fake.SHARE_SERVER, fake.IDENTIFIER, {})
|
||||
|
||||
mock_get_vserver_name.assert_called_once_with(fake.SHARE_SERVER['id'])
|
||||
self.assertEqual(fake_vserver_name, new_details['vserver_name'])
|
||||
self.assertEqual(fake_vserver_name, new_identifier)
|
||||
|
||||
def test_get_share_server_network_info(self):
|
||||
|
||||
fake_vserver_client = mock.Mock()
|
||||
|
||||
self.mock_object(context,
|
||||
'get_admin_context',
|
||||
mock.Mock(return_value='fake_admin_context'))
|
||||
mock_get_vserver = self.mock_object(
|
||||
self.library, '_get_vserver',
|
||||
mock.Mock(return_value=['fake', fake_vserver_client]))
|
||||
|
||||
net_interfaces = copy.deepcopy(c_fake.NETWORK_INTERFACES_MULTIPLE)
|
||||
|
||||
self.mock_object(fake_vserver_client,
|
||||
'get_network_interfaces',
|
||||
mock.Mock(return_value=net_interfaces))
|
||||
|
||||
result = self.library.get_share_server_network_info(context,
|
||||
fake.SHARE_SERVER,
|
||||
fake.IDENTIFIER,
|
||||
{})
|
||||
mock_get_vserver.assert_called_once_with(
|
||||
vserver_name=fake.IDENTIFIER
|
||||
)
|
||||
reference_allocations = []
|
||||
for lif in net_interfaces:
|
||||
reference_allocations.append(lif['address'])
|
||||
|
||||
self.assertEqual(reference_allocations, result)
|
||||
|
||||
@ddt.data((True, fake.IDENTIFIER),
|
||||
(False, fake.IDENTIFIER))
|
||||
@ddt.unpack
|
||||
def test__verify_share_server_name(self, vserver_exists, identifier):
|
||||
|
||||
mock_exists = self.mock_object(self.client, 'vserver_exists',
|
||||
mock.Mock(return_value=vserver_exists))
|
||||
expected_result = identifier
|
||||
if not vserver_exists:
|
||||
expected_result = self.library._get_vserver_name(identifier)
|
||||
|
||||
result = self.library._get_correct_vserver_old_name(identifier)
|
||||
|
||||
self.assertEqual(result, expected_result)
|
||||
mock_exists.assert_called_once_with(identifier)
|
||||
|
||||
def test_handle_housekeeping_tasks(self):
|
||||
|
||||
self.mock_object(self.client, 'prune_deleted_nfs_export_policies')
|
||||
|
|
|
@ -356,6 +356,7 @@ NETWORK_INFO = {
|
|||
NETWORK_INFO_NETMASK = '255.255.255.0'
|
||||
|
||||
SHARE_SERVER = {
|
||||
'id': 'fake_id',
|
||||
'share_network_id': 'c5b3a865-56d0-4d88-abe5-879965e099c9',
|
||||
'backend_details': {
|
||||
'vserver_name': VSERVER1
|
||||
|
@ -523,6 +524,8 @@ COLLATED_CGSNAPSHOT_INFO = [
|
|||
},
|
||||
]
|
||||
|
||||
IDENTIFIER = 'c5b3a865-56d0-4d88-dke5-853465e099c9'
|
||||
|
||||
LIF_NAMES = []
|
||||
LIF_ADDRESSES = ['10.10.10.10', '10.10.10.20']
|
||||
LIFS = (
|
||||
|
|
|
@ -163,10 +163,11 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
|
|||
self.assertEqual(fake.SHARE_ADDRESS_1, host_ip)
|
||||
self.assertEqual('/' + fake.SHARE_NAME, export_path)
|
||||
|
||||
def test_get_export_location_missing_location(self):
|
||||
@ddt.data('', 'invalid')
|
||||
def test_get_export_location_missing_location_invalid(self, export):
|
||||
|
||||
fake_share = fake.NFS_SHARE.copy()
|
||||
fake_share['export_location'] = ''
|
||||
fake_share['export_location'] = export
|
||||
|
||||
host_ip, export_path = self.helper._get_export_location(fake_share)
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- Added managing and unmanaging of share servers
|
||||
functionality to the NetApp driver, allowing for
|
||||
shares and snapshots to be managed and unmanaged in
|
||||
driver mode ``driver_handles_share_servers`` set to True.
|
Loading…
Reference in New Issue