Merge "[NetApp] Add manage/unmanage of share servers"

This commit is contained in:
Zuul 2019-03-07 13:32:45 +00:00 committed by Gerrit Code Review
commit a69f5333c4
12 changed files with 249 additions and 35 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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():

View File

@ -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')

View File

@ -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)

View File

@ -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')

View File

@ -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 = (

View File

@ -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)

View File

@ -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.