Remove the deprecated ibmnas driver

The ibmnas driver has been deprecated since the Liberty release.
In the Liberty release, NFS capabilities were added to the IBM
GPFS driver with commit:  bf4ca3bcf0 .
The ability for the GPFS driver to use NFS based shares rendered
the ibmnas driver redundant.

This commit removes the ibmnas driver and its tests.  The commit also
updates the opts.py file to remove reference to the ibmnas options.

Release notes are included with this commit.

DocImpact

Change-Id: Iac4a2f1770f7d02979eec1d8d5866d9fc4b6d18c
This commit is contained in:
Jay S Bryant 2015-12-17 11:53:45 -06:00
parent 94d4b15943
commit f63d321774
4 changed files with 5 additions and 869 deletions

View File

@ -109,8 +109,6 @@ from cinder.volume.drivers.ibm import flashsystem_fc as \
from cinder.volume.drivers.ibm import flashsystem_iscsi as \
cinder_volume_drivers_ibm_flashsystemiscsi
from cinder.volume.drivers.ibm import gpfs as cinder_volume_drivers_ibm_gpfs
from cinder.volume.drivers.ibm import ibmnas as \
cinder_volume_drivers_ibm_ibmnas
from cinder.volume.drivers.ibm.storwize_svc import storwize_svc_common as \
cinder_volume_drivers_ibm_storwize_svc_storwizesvccommon
from cinder.volume.drivers.ibm.storwize_svc import storwize_svc_fc as \
@ -226,7 +224,6 @@ def list_opts():
cinder_volume_drivers_sheepdog.sheepdog_opts,
[cinder_api_middleware_sizelimit.max_request_body_size_opt],
cinder_volume_drivers_solidfire.sf_opts,
cinder_volume_drivers_ibm_ibmnas.platform_opts,
cinder_backup_drivers_swift.swiftbackup_service_opts,
cinder_volume_drivers_cloudbyte_options.
cloudbyte_add_qosgroup_opts,

View File

@ -1,490 +0,0 @@
# Copyright 2013 IBM Corp.
#
# 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.
#
# Authors:
# Nilesh Bhosale <nilesh.bhosale@in.ibm.com>
# Sasikanth Eda <sasikanth.eda@in.ibm.com>
"""
Tests for the IBM NAS family (SONAS, Storwize V7000 Unified,
NAS based IBM GPFS Storage Systems).
"""
import mock
from oslo_config import cfg
from oslo_utils import units
from cinder import context
from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.ibm import ibmnas
CONF = cfg.CONF
class FakeEnv(object):
fields = {}
def __setitem__(self, key, value):
self.fields[key] = value
def __getitem__(self, item):
return self.fields[item]
class IBMNASDriverTestCase(test.TestCase):
TEST_NFS_EXPORT = 'nfs-host1:/export'
TEST_SIZE_IN_GB = 1
TEST_EXTEND_SIZE_IN_GB = 2
TEST_MNT_POINT = '/mnt/nfs'
TEST_MNT_POINT_BASE = '/mnt'
TEST_LOCAL_PATH = '/mnt/nfs/volume-123'
TEST_VOLUME_PATH = '/export/volume-123'
TEST_SNAP_PATH = '/export/snapshot-123'
def setUp(self):
super(IBMNASDriverTestCase, self).setUp()
self._driver = ibmnas.IBMNAS_NFSDriver(configuration=
conf.Configuration(None))
self._mock = mock.Mock()
self._def_flags = {'nas_ip': 'hostname',
'nas_login': 'user',
'nas_ssh_port': 22,
'nas_password': 'pass',
'nas_private_key': 'nas.key',
'ibmnas_platform_type': 'v7ku',
'nfs_shares_config': None,
'nfs_sparsed_volumes': True,
'nfs_used_ratio': 0.95,
'nfs_oversub_ratio': 1.0,
'nfs_mount_point_base':
self.TEST_MNT_POINT_BASE,
'nfs_mount_options': None}
self.context = context.get_admin_context()
self.context.user_id = 'fake'
self.context.project_id = 'fake'
def _set_flag(self, flag, value):
group = self._driver.configuration.config_group
self._driver.configuration.set_override(flag, value, group)
def _reset_flags(self):
self._driver.configuration.local_conf.reset()
for k, v in self._def_flags.items():
self._set_flag(k, v)
def test_check_for_setup_error(self):
"""Check setup with bad parameters."""
drv = self._driver
required_flags = [
'nas_ip',
'nas_login',
'nas_ssh_port']
for flag in required_flags:
self._set_flag(flag, None)
self.assertRaises(exception.CinderException,
drv.check_for_setup_error)
self._set_flag('nas_password', None)
self._set_flag('nas_private_key', None)
self.assertRaises(exception.InvalidInput,
self._driver.check_for_setup_error)
self._set_flag('ibmnas_platform_type', None)
self.assertRaises(exception.InvalidInput,
self._driver.check_for_setup_error)
self._reset_flags()
def test_get_provider_location(self):
"""Check provider location for given volume id."""
mock = self._mock
volume = FakeEnv()
volume['id'] = '123'
mock.drv._get_provider_location.return_value = self.TEST_NFS_EXPORT
self.assertEqual(self.TEST_NFS_EXPORT,
mock.drv._get_provider_location(volume['id']))
def test_get_export_path(self):
"""Check export path for the given volume."""
mock = self._mock
volume = FakeEnv()
volume['id'] = '123'
mock.drv._get_export_path.return_value = self.TEST_NFS_EXPORT.\
split(':')[1]
self.assertEqual(self.TEST_NFS_EXPORT.split(':')[1],
mock.drv._get_export_path(volume['id']))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ensure_shares_mounted')
def test_update_volume_stats(self, mock_ensure):
"""Check update volume stats."""
drv = self._driver
mock_ensure.return_value = True
fake_avail = 80 * units.Gi
fake_size = 2 * fake_avail
fake_used = 10 * units.Gi
with mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_capacity_info',
return_value=(fake_avail, fake_size, fake_used)):
stats = drv.get_volume_stats()
self.assertEqual('IBMNAS_NFS', stats['volume_backend_name'])
self.assertEqual('nfs', stats['storage_protocol'])
self.assertEqual('1.1.0', stats['driver_version'])
self.assertEqual('IBM', stats['vendor_name'])
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
def test_ssh_operation(self, mock_ssh):
drv = self._driver
mock_ssh.return_value = None
self.assertIsNone(drv._ssh_operation('ssh_cmd'))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
def test_ssh_operation_exception(self, mock_ssh):
drv = self._driver
mock_ssh.side_effect = (
exception.VolumeBackendAPIException(data='Failed'))
self.assertRaises(exception.VolumeBackendAPIException,
drv._ssh_operation, 'ssh_cmd')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
@mock.patch('oslo_concurrency.processutils.execute')
def test_create_ibmnas_snap_mount_point_provided(self, mock_ssh,
mock_execute):
"""Create ibmnas snap if mount point is provided."""
drv = self._driver
mock_ssh.return_value = True
mock_execute.return_value = True
self.assertIsNone(drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
self.TEST_SNAP_PATH,
self.TEST_MNT_POINT))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
@mock.patch('oslo_concurrency.processutils.execute')
def test_create_ibmnas_snap_nas_gpfs(self, mock_execute, mock_ssh):
"""Create ibmnas snap if mount point is provided."""
drv = self._driver
drv.configuration.platform = 'gpfs-nas'
mock_ssh.return_value = True
mock_execute.return_value = True
self.assertIsNone(drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
self.TEST_SNAP_PATH,
self.TEST_MNT_POINT))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
def test_create_ibmnas_snap_no_mount_point_provided(self, mock_ssh):
"""Create ibmnas snap if no mount point is provided."""
drv = self._driver
mock_ssh.return_value = True
self.assertIsNone(drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
self.TEST_SNAP_PATH,
None))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
def test_create_ibmnas_snap_nas_gpfs_no_mount(self, mock_ssh):
"""Create ibmnas snap (gpfs-nas) if mount point is provided."""
drv = self._driver
drv.configuration.platform = 'gpfs-nas'
mock_ssh.return_value = True
drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
self.TEST_SNAP_PATH, None)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
def test_create_ibmnas_copy(self, mock_ssh):
"""Create ibmnas copy test case."""
drv = self._driver
TEST_DEST_SNAP = '/export/snapshot-123.snap'
TEST_DEST_PATH = '/export/snapshot-123'
mock_ssh.return_value = True
drv._create_ibmnas_copy(self.TEST_VOLUME_PATH,
TEST_DEST_PATH,
TEST_DEST_SNAP)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_ssh_operation')
def test_create_ibmnas_copy_nas_gpfs(self, mock_ssh):
"""Create ibmnas copy for gpfs-nas platform test case."""
drv = self._driver
TEST_DEST_SNAP = '/export/snapshot-123.snap'
TEST_DEST_PATH = '/export/snapshot-123'
drv.configuration.platform = 'gpfs-nas'
mock_ssh.return_value = True
drv._create_ibmnas_copy(self.TEST_VOLUME_PATH,
TEST_DEST_PATH,
TEST_DEST_SNAP)
@mock.patch('cinder.image.image_utils.resize_image')
def test_resize_volume_file(self, mock_size):
"""Resize volume file test case."""
drv = self._driver
mock_size.return_value = True
self.assertTrue(drv._resize_volume_file(self.TEST_LOCAL_PATH,
self.TEST_EXTEND_SIZE_IN_GB))
@mock.patch('cinder.image.image_utils.resize_image')
def test_resize_volume_exception(self, mock_size):
"""Resize volume file test case."""
drv = self._driver
mock_size.side_effect = (
exception.VolumeBackendAPIException(data='Failed'))
self.assertRaises(exception.VolumeBackendAPIException,
drv._resize_volume_file,
self.TEST_LOCAL_PATH,
self.TEST_EXTEND_SIZE_IN_GB)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_resize_volume_file')
def test_extend_volume(self, mock_resize, mock_local):
"""Extend volume to greater size test case."""
drv = self._driver
mock_local.return_value = self.TEST_LOCAL_PATH
mock_resize.return_value = True
volume = FakeEnv()
volume['name'] = 'vol-123'
drv.extend_volume(volume,
self.TEST_EXTEND_SIZE_IN_GB)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
@mock.patch('oslo_concurrency.processutils.execute')
def test_delete_snapfiles(self, mock_execute, mock_ssh):
"""Delete_snapfiles test case."""
drv = self._driver
expected = ('Parent Depth Parent inode'
'File name\n yes 0 /ibm/gpfs0/gshare/\n'
'volume-123\n EFSSG1000I The command'
'completed successfully.', '')
mock_ssh.return_value = expected
mock_execute.return_value = expected
drv._delete_snapfiles(self.TEST_VOLUME_PATH,
self.TEST_MNT_POINT)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
@mock.patch('oslo_concurrency.processutils.execute')
def test_delete_snapfiles_nas_gpfs(self, mock_execute, mock_ssh):
"""Delete_snapfiles for gpfs-nas platform test case."""
drv = self._driver
drv.configuration.platform = 'gpfs-nas'
expected = ('Parent Depth Parent inode'
'File name\n'
'------ ----- -------------'
'- ---------\n'
'yes 0\n'
'/ibm/gpfs0/gshare/volume-123', '')
mock_ssh.return_value = expected
mock_execute.return_value = expected
drv._delete_snapfiles(self.TEST_VOLUME_PATH,
self.TEST_MNT_POINT)
def test_delete_volume_no_provider_location(self):
"""Delete volume with no provider location specified."""
drv = self._driver
volume = FakeEnv()
volume['name'] = 'volume-123'
volume['provider_location'] = None
result = drv.delete_volume(volume)
self.assertIsNone(result)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_export_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_delete_snapfiles')
def test_delete_volume(self, mock_snap, mock_export):
"""Delete volume test case."""
drv = self._driver
mock_export.return_value = self.TEST_VOLUME_PATH
mock_snap.return_value = True
volume = FakeEnv()
volume['id'] = '123'
volume['name'] = '/volume-123'
volume['provider_location'] = self.TEST_VOLUME_PATH
self.assertIsNone(drv.delete_volume(volume))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_export_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_provider_location')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_mount_point_for_share')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_create_ibmnas_snap')
def test_create_snapshot(self, mock_snap, mock_mount, mock_provider,
mock_export):
"""Create snapshot simple test case."""
drv = self._driver
mock_export.return_value = self.TEST_LOCAL_PATH
mock_provider.return_value = self.TEST_VOLUME_PATH
mock_mount.return_value = self.TEST_MNT_POINT
mock_snap.return_value = True
volume = FakeEnv()
volume['id'] = '123'
volume['name'] = 'volume-123'
snapshot = FakeEnv()
snapshot['volume_id'] = volume['id']
snapshot['volume_name'] = '/volume-123'
snapshot['name'] = '/snapshot-123'
drv.create_snapshot(snapshot)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_provider_location')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_mount_point_for_share')
@mock.patch('oslo_concurrency.processutils.execute')
def test_delete_snapshot(self, mock_execute, mock_mount, mock_provider):
"""Delete snapshot simple test case."""
drv = self._driver
mock_provider.return_value = self.TEST_VOLUME_PATH
mock_mount.return_value = self.TEST_LOCAL_PATH
mock_execute.return_value = True
volume = FakeEnv()
volume['id'] = '123'
volume['provider_location'] = self.TEST_NFS_EXPORT
snapshot = FakeEnv()
snapshot['volume_id'] = volume['id']
snapshot['volume_name'] = 'volume-123'
snapshot['name'] = 'snapshot-123'
drv.delete_snapshot(snapshot)
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_export_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_create_ibmnas_copy')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_find_share')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_set_rw_permissions_for_owner')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_resize_volume_file')
def test_create_cloned_volume(self, mock_resize, mock_rw, mock_local,
mock_find, mock_copy, mock_export):
"""Clone volume with equal size test case."""
drv = self._driver
mock_export.return_value = self.TEST_VOLUME_PATH
mock_copy.return_value = True
mock_find.return_value = self.TEST_LOCAL_PATH
mock_local.return_value = self.TEST_LOCAL_PATH
mock_rw.return_value = True
mock_resize.return_value = True
volume_src = FakeEnv()
volume_src['id'] = '123'
volume_src['name'] = '/volume-123'
volume_src.size = self.TEST_SIZE_IN_GB
volume_dest = FakeEnv()
volume_dest['id'] = '456'
volume_dest['name'] = '/volume-456'
volume_dest['size'] = self.TEST_SIZE_IN_GB
volume_dest.size = self.TEST_SIZE_IN_GB
self.assertEqual({'provider_location': self.TEST_LOCAL_PATH},
drv.create_cloned_volume(volume_dest, volume_src))
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_get_export_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_create_ibmnas_snap')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_find_share')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_set_rw_permissions_for_owner')
@mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
'_resize_volume_file')
def test_create_volume_from_snapshot(self, mock_resize, mock_rw,
mock_local, mock_find, mock_snap,
mock_export):
"""Create volume from snapshot test case."""
drv = self._driver
mock_export.return_value = '/export'
mock_snap.return_value = self.TEST_LOCAL_PATH
mock_find.return_value = self.TEST_LOCAL_PATH
mock_local.return_value = self.TEST_VOLUME_PATH
mock_rw.return_value = True
mock_resize.return_value = True
volume = FakeEnv()
volume['id'] = '123'
volume['name'] = '/volume-123'
volume['size'] = self.TEST_SIZE_IN_GB
snapshot = FakeEnv()
snapshot['volume_id'] = volume['id']
snapshot['volume_name'] = 'volume-123'
snapshot['volume_size'] = self.TEST_SIZE_IN_GB
snapshot.name = '/snapshot-123'
self.assertEqual({'provider_location': self.TEST_LOCAL_PATH},
drv.create_volume_from_snapshot(volume, snapshot))

View File

@ -1,376 +0,0 @@
# Copyright 2014 IBM Corp.
#
# 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.
# Authors:
# Nilesh Bhosale <nilesh.bhosale@in.ibm.com>
# Sasikanth Eda <sasikanth.eda@in.ibm.com>
"""
IBM NAS Volume Driver.
Currently, it supports the following IBM Storage Systems:
1. IBM Scale Out NAS (SONAS)
2. IBM Storwize V7000 Unified
3. NAS based IBM GPFS Storage Systems
Notes:
1. If you specify both a password and a key file, this driver will use the
key file only.
2. When using a key file for authentication, it is up to the user or
system administrator to store the private key in a safe manner.
"""
import os
import re
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import units
import six
from cinder import exception
from cinder.i18n import _, _LI, _LW
from cinder.image import image_utils
from cinder import utils
from cinder.volume.drivers import nfs
from cinder.volume.drivers import remotefs
from cinder.volume.drivers.san import san
VERSION = '1.1.0'
LOG = logging.getLogger(__name__)
platform_opts = [
cfg.StrOpt('ibmnas_platform_type',
default='v7ku',
choices=['v7ku', 'sonas', 'gpfs-nas'],
help=('IBMNAS platform type to be used as backend storage; '
'valid values are - '
'v7ku : for using IBM Storwize V7000 Unified, '
'sonas : for using IBM Scale Out NAS, '
'gpfs-nas : for using NFS based IBM GPFS deployments.')),
]
CONF = cfg.CONF
CONF.register_opts(platform_opts)
class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
"""IBMNAS NFS based cinder driver.
Creates file on NFS share for using it as block device on hypervisor.
Version history:
1.0.0 - Initial driver
1.1.0 - Support for NFS based GPFS storage backend
"""
driver_volume_type = 'nfs'
VERSION = VERSION
def __init__(self, execute=utils.execute, *args, **kwargs):
self._context = None
super(IBMNAS_NFSDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(remotefs.nas_opts)
self.configuration.append_config_values(platform_opts)
self.configuration.san_ip = self.configuration.nas_ip
self.configuration.san_login = self.configuration.nas_login
self.configuration.san_password = self.configuration.nas_password
self.configuration.san_private_key = \
self.configuration.nas_private_key
self.configuration.san_ssh_port = self.configuration.nas_ssh_port
self.configuration.ibmnas_platform_type = \
self.configuration.ibmnas_platform_type.lower()
self._execute = execute
LOG.info(_LI('Initialized driver for IBMNAS Platform: %s.'),
self.configuration.ibmnas_platform_type)
def do_setup(self, context):
"""Any initialization the volume driver does while starting."""
super(IBMNAS_NFSDriver, self).do_setup(context)
self._context = context
def check_for_setup_error(self):
"""Ensure that the flags are set properly."""
required_flags = ['nas_ip', 'nas_ssh_port', 'nas_login',
'ibmnas_platform_type']
valid_platforms = ['v7ku', 'sonas', 'gpfs-nas']
for flag in required_flags:
if not self.configuration.safe_get(flag):
raise exception.InvalidInput(reason=_('%s is not set') % flag)
# Ensure that either password or keyfile were set
if not (self.configuration.nas_password or
self.configuration.nas_private_key):
raise exception.InvalidInput(
reason=_('Password or SSH private key is required for '
'authentication: set either nas_password or '
'nas_private_key option'))
# Ensure whether ibmnas platform type is set to appropriate value
if self.configuration.ibmnas_platform_type not in valid_platforms:
raise exception.InvalidInput(
reason = (_("Unsupported ibmnas_platform_type: %(given)s."
" Supported platforms: %(valid)s")
% {'given': self.configuration.ibmnas_platform_type,
'valid': (', '.join(valid_platforms))}))
def _get_provider_location(self, volume_id):
"""Returns provider location for given volume."""
LOG.debug("Enter _get_provider_location: volume_id %s", volume_id)
volume = self.db.volume_get(self._context, volume_id)
LOG.debug("Exit _get_provider_location")
return volume['provider_location']
def _get_export_path(self, volume_id):
"""Returns NFS export path for the given volume."""
LOG.debug("Enter _get_export_path: volume_id %s", volume_id)
return self._get_provider_location(volume_id).split(':')[1]
def _update_volume_stats(self):
"""Retrieve stats info from volume group."""
LOG.debug("Enter _update_volume_stats")
data = {}
backend_name = self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = backend_name or 'IBMNAS_NFS'
data['vendor_name'] = 'IBM'
data['driver_version'] = self.get_version()
data['storage_protocol'] = self.driver_volume_type
self._ensure_shares_mounted()
global_capacity = 0
global_free = 0
for share in self._mounted_shares:
capacity, free, _used = self._get_capacity_info(share)
global_capacity += capacity
global_free += free
data['total_capacity_gb'] = global_capacity / float(units.Gi)
data['free_capacity_gb'] = global_free / float(units.Gi)
data['reserved_percentage'] = 0
data['QoS_support'] = False
self._stats = data
LOG.debug("Exit _update_volume_stats")
def _ssh_operation(self, ssh_cmd):
try:
self._run_ssh(ssh_cmd)
except processutils.ProcessExecutionError as e:
msg = (_('Failed in _ssh_operation while execution of ssh_cmd:'
'%(cmd)s. Error: %(error)s') %
{'cmd': ssh_cmd, 'error': six.text_type(e)})
LOG.exception(msg)
raise exception.VolumeBackendAPIException(data=msg)
def _create_ibmnas_snap(self, src, dest, mount_path):
"""Create volume clones and snapshots."""
LOG.debug("Enter _create_ibmnas_snap: src %(src)s, dest %(dest)s",
{'src': src, 'dest': dest})
if self.configuration.ibmnas_platform_type == 'gpfs-nas':
ssh_cmd = ['mmclone', 'snap', src, dest]
self._ssh_operation(ssh_cmd)
else:
if mount_path is not None:
tmp_file_path = dest + '.snap'
ssh_cmd = ['mkclone', '-p', dest, '-s', src, '-t',
tmp_file_path]
try:
self._ssh_operation(ssh_cmd)
finally:
# Now remove the tmp file
tmp_file_local_path = os.path.join(mount_path, os.path.
basename(tmp_file_path))
self._execute('rm', '-f', tmp_file_local_path,
run_as_root=True)
else:
ssh_cmd = ['mkclone', '-s', src, '-t', dest]
self._ssh_operation(ssh_cmd)
LOG.debug("Exit _create_ibmnas_snap")
def _create_ibmnas_copy(self, src, dest, snap):
"""Create a cloned volume, parent & the clone both remain writable."""
LOG.debug('Enter _create_ibmnas_copy: src %(src)s, dest %(dest)s, '
'snap %(snap)s', {'src': src,
'dest': dest,
'snap': snap})
if self.configuration.ibmnas_platform_type == 'gpfs-nas':
ssh_cmd = ['mmclone', 'snap', src, snap]
self._ssh_operation(ssh_cmd)
ssh_cmd = ['mmclone', 'copy', snap, dest]
self._ssh_operation(ssh_cmd)
else:
ssh_cmd = ['mkclone', '-p', snap, '-s', src, '-t', dest]
self._ssh_operation(ssh_cmd)
LOG.debug("Exit _create_ibmnas_copy")
def _resize_volume_file(self, path, new_size):
"""Resize the image file on share to new size."""
LOG.debug("Resizing file to %sG.", new_size)
try:
image_utils.resize_image(path, new_size, run_as_root=True)
except processutils.ProcessExecutionError as e:
msg = (_("Failed to resize volume "
"%(volume_id)s, error: %(error)s") %
{'volume_id': os.path.basename(path).split('-')[1],
'error': six.text_type(e.stderr)})
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
return True
def extend_volume(self, volume, new_size):
"""Extend an existing volume to the new size."""
LOG.debug("Extending volume %s", volume['name'])
path = self.local_path(volume)
self._resize_volume_file(path, new_size)
def _delete_snapfiles(self, fchild, mount_point):
LOG.debug('Enter _delete_snapfiles: fchild %(fchild)s, '
'mount_point %(mount_point)s',
{'fchild': fchild,
'mount_point': mount_point})
if self.configuration.ibmnas_platform_type == 'gpfs-nas':
ssh_cmd = ['mmclone', 'show', fchild]
else:
ssh_cmd = ['lsclone', fchild]
try:
(out, _err) = self._run_ssh(ssh_cmd, check_exit_code=False)
except processutils.ProcessExecutionError as e:
msg = (_("Failed in _delete_snapfiles. Error: %s") %
six.text_type(e.stderr))
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
fparent = None
reInode = re.compile(
r'.*\s+(?:yes|no)\s+\d+\s+(?P<inode>\d+)', re.M | re.S)
match = reInode.match(out)
if match:
inode = match.group('inode')
path = mount_point
(out, _err) = self._execute('find', path, '-maxdepth', '1',
'-inum', inode, run_as_root=True)
if out:
fparent = out.split('\n', 1)[0]
fchild_local_path = os.path.join(mount_point, os.path.basename(fchild))
self._execute(
'rm', '-f', fchild_local_path, check_exit_code=False,
run_as_root=True)
# There is no need to check for volume references on this snapshot
# because 'rm -f' itself serves as a simple and implicit check. If the
# parent is referenced by another volume, system doesn't allow deleting
# it. 'rm -f' silently fails and the subsequent check on the path
# indicates whether there are any volumes derived from that snapshot.
# If there are such volumes, we quit recursion and let the other
# volumes delete the snapshot later. If there are no references, rm
# would succeed and the snapshot is deleted.
if not os.path.exists(fchild) and fparent:
fpbase = os.path.basename(fparent)
if (fpbase.endswith('.ts') or fpbase.endswith('.snap')):
fparent_remote_path = os.path.join(os.path.dirname(fchild),
fpbase)
self._delete_snapfiles(fparent_remote_path, mount_point)
LOG.debug("Exit _delete_snapfiles")
def delete_volume(self, volume):
"""Deletes a logical volume."""
if not volume['provider_location']:
LOG.warning(_LW('Volume %s does not have '
'provider_location specified, '
'skipping.'), volume['name'])
return
export_path = self._get_export_path(volume['id'])
volume_name = volume['name']
volume_path = os.path.join(export_path, volume_name)
mount_point = os.path.dirname(self.local_path(volume))
# Delete all dependent snapshots, the snapshot will get deleted
# if the link count goes to zero, else rm will fail silently
self._delete_snapfiles(volume_path, mount_point)
def create_snapshot(self, snapshot):
"""Creates a volume snapshot."""
export_path = self._get_export_path(snapshot['volume_id'])
snapshot_path = os.path.join(export_path, snapshot['name'])
volume_path = os.path.join(export_path, snapshot['volume_name'])
nfs_share = self._get_provider_location(snapshot['volume_id'])
mount_path = self._get_mount_point_for_share(nfs_share)
self._create_ibmnas_snap(src=volume_path, dest=snapshot_path,
mount_path=mount_path)
def delete_snapshot(self, snapshot):
"""Deletes a volume snapshot."""
# A snapshot file is deleted as a part of delete_volume when
# all volumes derived from it are deleted.
# Rename the deleted snapshot to indicate it no longer exists in
# cinder db. Attempt to delete the snapshot. If the snapshot has
# clone children, the delete will fail silently. When volumes that
# are clone children are deleted in the future, the remaining ts
# snapshots will also be deleted.
nfs_share = self._get_provider_location(snapshot['volume_id'])
mount_path = self._get_mount_point_for_share(nfs_share)
snapshot_path = os.path.join(mount_path, snapshot['name'])
snapshot_ts_path = '%s.ts' % snapshot_path
self._execute('mv', '-f', snapshot_path, snapshot_ts_path,
check_exit_code=True, run_as_root=True)
self._execute('rm', '-f', snapshot_ts_path,
check_exit_code=False, run_as_root=True)
def create_volume_from_snapshot(self, volume, snapshot):
"""Creates a volume from an existing volume snapshot.
Extends the volume if the volume size is more than the snapshot size.
"""
export_path = self._get_export_path(snapshot['volume_id'])
snapshot_path = os.path.join(export_path, snapshot.name)
volume_path = os.path.join(export_path, volume['name'])
if self.configuration.ibmnas_platform_type == 'gpfs-nas':
ssh_cmd = ['mmclone', 'copy', snapshot_path, volume_path]
self._ssh_operation(ssh_cmd)
else:
self._create_ibmnas_snap(snapshot_path, volume_path, None)
volume['provider_location'] = self._find_share(volume['size'])
volume_path = self.local_path(volume)
self._set_rw_permissions_for_owner(volume_path)
# Extend the volume if required
self._resize_volume_file(volume_path, volume['size'])
return {'provider_location': volume['provider_location']}
def create_cloned_volume(self, volume, src_vref):
"""Creates a clone of the specified volume.
Extends the volume if the new volume size is more than
the source volume size.
"""
export_path = self._get_export_path(src_vref['id'])
src_vol_path = os.path.join(export_path, src_vref['name'])
dest_vol_path = os.path.join(export_path, volume['name'])
snap_file_name = volume['name']
snap_file_name = snap_file_name + '.snap'
snap_file_path = os.path.join(export_path, snap_file_name)
self._create_ibmnas_copy(src_vol_path, dest_vol_path, snap_file_path)
volume['provider_location'] = self._find_share(volume['size'])
volume_path = self.local_path(volume)
self._set_rw_permissions_for_owner(volume_path)
# Extend the volume if required
self._resize_volume_file(volume_path, volume['size'])
return {'provider_location': volume['provider_location']}

View File

@ -0,0 +1,5 @@
---
upgrade:
- Users of the ibmnas driver should switch to using the IBM GPFS driver to enable Cinder access to IBM NAS resources. For details configuring the IBM GPFS driver, see the GPFS config reference. - http://docs.openstack.org/liberty/config-reference/content/GPFS-driver.html
other:
- Due to the ibmnas (SONAS) driver being rendered redundant by the addition of NFS capabilities to the IBM GPFS driver, the ibmnas driver is being removed in the Mitaka release.