diff --git a/manila/share/drivers/netapp/cluster_mode.py b/manila/share/drivers/netapp/cluster_mode.py index 09fc8b391a..dfe90b1d78 100644 --- a/manila/share/drivers/netapp/cluster_mode.py +++ b/manila/share/drivers/netapp/cluster_mode.py @@ -161,17 +161,6 @@ class NetAppClusteredShareDriver(driver.NetAppShareDriver): return int(self._client.send_request( 'system-node-get-iter').get_child_content('num-records')) - def _delete_vserver(self, vserver_name, vserver_client): - """Deletes vserver.""" - vserver_client.send_request( - 'volume-offline', - {'name': self.configuration.netapp_root_volume_name}) - vserver_client.send_request( - 'volume-destroy', - {'name': self.configuration.netapp_root_volume_name}) - args = {'vserver-name': vserver_name} - self._client.send_request('vserver-destroy', args) - def _create_net_iface(self, ip, netmask, vlan, node, port, vserver_name, allocation_id): """Creates lif on vlan port.""" @@ -228,17 +217,23 @@ class NetAppClusteredShareDriver(driver.NetAppShareDriver): return self.configuration.netapp_vserver_name_template \ % {'net_id': net_id} + def _vserver_exists(self, vserver_name): + args = {'query': {'vserver-info': {'vserver-name': vserver_name}}} + + LOG.debug(_('Checking if vserver exists')) + vserver_info = self._client.send_request('vserver-get-iter', args) + if int(vserver_info.get_child_content('num-records')): + return True + else: + return False + def _vserver_create_if_not_exists(self, network_info): """Creates vserver if not exists with given parameters.""" vserver_name = self._get_vserver_name(network_info['id']) vserver_client = driver.NetAppApiClient( self.api_version, vserver=vserver_name, configuration=self.configuration) - args = {'query': {'vserver-info': {'vserver-name': vserver_name}}} - - LOG.debug(_('Checking if vserver is configured')) - vserver_info = self._client.send_request('vserver-get-iter', args) - if not int(vserver_info.get_child_content('num-records')): + if not self._vserver_exists(vserver_name): LOG.debug(_('Vserver %s does not exist, creating') % vserver_name) self._create_vserver(vserver_name) nodes = self._get_cluster_nodes() @@ -626,6 +621,65 @@ class NetAppClusteredShareDriver(driver.NetAppShareDriver): helper.set_client(vserver_client) return helper.deny_access(context, share, access) + def _delete_vserver(self, vserver_name, vserver_client, + network_info=None): + """ + Delete vserver. + + Checks if vserver exists and does not have active shares. + Offlines and destroys root volumes. + Deletes vserver. + """ + if not self._vserver_exists(vserver_name): + LOG.error(_("Vserver %s does not exists.") % vserver_name) + return + volumes_data = vserver_client.send_request('volume-get-iter') + volumes_count = int(volumes_data.get_child_content('num-records')) + if volumes_count == 1: + try: + vserver_client.send_request( + 'volume-offline', + {'name': self.configuration.netapp_root_volume_name}) + except naapi.NaApiError as e: + if e.code == '13042': + LOG.error(_("Volume %s is already offline.") + % self.configuration.netapp_root_volume_name) + else: + raise e + vserver_client.send_request( + 'volume-destroy', + {'name': self.configuration.netapp_root_volume_name}) + elif volumes_count > 1: + msg = _("Error deleting vserver. " + "Vserver %s has shares.") % vserver_name + LOG.error(msg) + raise exception.NetAppException(msg) + if network_info and network_info.get('security_services'): + for service in network_info['security_services']: + if service['type'] == 'active_directory': + args = {'admin-password': service['password'], + 'admin-username': service['sid']} + try: + vserver_client.send_request('cifs-server-delete', + args) + except naapi.NaApiError as e: + if e.code == "15661": + LOG.error(_("Cifs server does not exists for" + " vserver %s") % vserver_name) + else: + vserver_client.send_request('cifs-server-delete') + self._client.send_request('vserver-destroy', + {'vserver-name': vserver_name}) + + def teardown_network(self, network_info): + """Teardown share network.""" + vserver_name = self._get_vserver_name(network_info['id']) + vserver_client = driver.NetAppApiClient( + self.api_version, vserver=vserver_name, + configuration=self.configuration) + self._delete_vserver(vserver_name, vserver_client, + network_info=network_info) + class NetAppClusteredNFSHelper(driver.NetAppNFSHelper): """Netapp specific cluster-mode NFS sharing driver.""" diff --git a/manila/tests/netapp/test_cmode_drv.py b/manila/tests/netapp/test_cmode_drv.py index 1195cad53a..7225c9fa7d 100644 --- a/manila/tests/netapp/test_cmode_drv.py +++ b/manila/tests/netapp/test_cmode_drv.py @@ -17,6 +17,7 @@ import hashlib import mock from manila import context +from manila import exception from manila.share import configuration from manila.share.drivers.netapp import api as naapi from manila.share.drivers.netapp import cluster_mode as driver @@ -102,7 +103,11 @@ class NetAppClusteredDrvTestCase(test.TestCase): self.driver._client.send_request = mock.Mock(return_value=res) self.assertEqual(self.driver.get_network_allocations_number(), 5) - def test_delete_vserver(self): + def test_delete_vserver_without_net_info(self): + el = naapi.NaElement('fake') + el['num-records'] = 1 + self.driver._vserver_exists = mock.Mock(return_value=True) + self._vserver_client.send_request = mock.Mock(return_value=el) self.driver._delete_vserver('fake', self._vserver_client) self._vserver_client.send_request.assert_has_calls([ mock.call('volume-offline', {'name': 'root'}), @@ -111,6 +116,81 @@ class NetAppClusteredDrvTestCase(test.TestCase): self.driver._client.send_request.assert_called_once_with( 'vserver-destroy', {'vserver-name': 'fake'}) + def test_delete_vserver_with_net_info(self): + el = naapi.NaElement('fake') + el['num-records'] = 1 + self.driver._vserver_exists = mock.Mock(return_value=True) + self._vserver_client.send_request = mock.Mock(return_value=el) + net_info = { + 'security_services': [ + {'sid': 'admin', + 'password': 'pass', + 'type': 'active_directory'} + ] + } + self.driver._delete_vserver('fake', self._vserver_client, + network_info=net_info) + self._vserver_client.send_request.assert_has_calls([ + mock.call('volume-get-iter'), + mock.call('volume-offline', {'name': 'root'}), + mock.call('volume-destroy', {'name': 'root'}), + mock.call('cifs-server-delete', {'admin-username': 'admin', + 'admin-password': 'pass'}) + ]) + self.driver._client.send_request.assert_called_once_with( + 'vserver-destroy', {'vserver-name': 'fake'}) + + def test_delete_vserver_has_shares(self): + el = naapi.NaElement('fake') + el['num-records'] = 3 + self.driver._vserver_exists = mock.Mock(return_value=True) + self._vserver_client.send_request = mock.Mock(return_value=el) + self.assertRaises(exception.NetAppException, + self.driver._delete_vserver, 'fake', + self._vserver_client) + + def test_delete_vserver_without_root_volume(self): + el = naapi.NaElement('fake') + el['num-records'] = '0' + self.driver._vserver_exists = mock.Mock(return_value=True) + self._vserver_client.send_request = mock.Mock(return_value=el) + self.driver._delete_vserver('fake', self._vserver_client) + self.driver._client.send_request.assert_called_once_with( + 'vserver-destroy', {'vserver-name': 'fake'}) + + def test_delete_vserver_does_not_exists(self): + self.driver._vserver_exists = mock.Mock(return_value=False) + self.driver._delete_vserver('fake', self._vserver_client) + self.assertEqual(self.driver._client.send_request.called, False) + + def test_vserver_exists_true(self): + el = naapi.NaElement('fake') + el['num-records'] = '0' + self.driver._client.send_request = mock.Mock(return_value=el) + self.assertEqual(self.driver._vserver_exists('fake_vserver'), False) + self.driver._client.send_request.assert_called_once_with( + 'vserver-get-iter', {'query': { + 'vserver-info': { + 'vserver-name': 'fake_vserver' + } + } + } + ) + + def test_vserver_exists_false(self): + el = naapi.NaElement('fake') + el['num-records'] = '1' + self.driver._client.send_request = mock.Mock(return_value=el) + self.assertEqual(self.driver._vserver_exists('fake_vserver'), True) + self.driver._client.send_request.assert_called_once_with( + 'vserver-get-iter', {'query': { + 'vserver-info': { + 'vserver-name': 'fake_vserver' + } + } + } + ) + def test_create_net_iface(self): self.driver._create_net_iface('1.1.1.1', '255.255.255.0', '200', 'node', 'port', 'vserver-name', 'all_id') @@ -297,6 +377,13 @@ class NetAppClusteredDrvTestCase(test.TestCase): self.helper.deny_access.assert_called_ince_with(self._context, self.share, access) + def test_teardown_network(self): + fake_net_info = {'id': 'fakeid'} + self.driver._delete_vserver = mock.Mock() + self.driver.teardown_network(fake_net_info) + self.driver._delete_vserver.assert_called_once_with( + 'os_fakeid', self._vserver_client, network_info=fake_net_info) + class NetAppNFSHelperTestCase(test.TestCase): """Tests for NetApp 7mode driver.