Revert "Remove Tegile volume driver"

This reverts commit 4679d1fae0.

Tegile Volume driver was removed because CI was not running.

CI was down due to hardware inavailability. The CI systems
are up and running now.

Change-Id: Ie098a1b5330921426227543ad52b5ffed031fa85
Implements: blueprint tegile-volume-driver
This commit is contained in:
Abhilash Divakaran 2016-08-08 11:03:21 -07:00
parent 4559058147
commit 99822262b0
5 changed files with 1077 additions and 4 deletions

View File

@ -1247,6 +1247,11 @@ class CohoException(VolumeDriverException):
message = _("Coho Data Cinder driver failure: %(message)s")
# Tegile Storage drivers
class TegileAPIException(VolumeBackendAPIException):
message = _("Unexpected response from Tegile IntelliFlash API")
# NexentaStor driver exception
class NexentaException(VolumeDriverException):
message = _("%(message)s")

View File

@ -160,6 +160,7 @@ from cinder.volume.drivers import smbfs as cinder_volume_drivers_smbfs
from cinder.volume.drivers import solidfire as cinder_volume_drivers_solidfire
from cinder.volume.drivers.synology import synology_common as \
cinder_volume_drivers_synology_synologycommon
from cinder.volume.drivers import tegile as cinder_volume_drivers_tegile
from cinder.volume.drivers import tintri as cinder_volume_drivers_tintri
from cinder.volume.drivers.violin import v7000_common as \
cinder_volume_drivers_violin_v7000common
@ -347,6 +348,7 @@ def list_opts():
cinder_volume_manager.volume_manager_opts,
cinder_volume_drivers_ibm_flashsystemiscsi.
flashsystem_iscsi_opts,
cinder_volume_drivers_tegile.tegile_opts,
cinder_volume_drivers_ibm_flashsystemcommon.flashsystem_opts,
[cinder_volume_api.allow_force_upload_opt],
[cinder_volume_api.volume_host_opt],

View File

@ -0,0 +1,408 @@
# Copyright (c) 2015 by Tegile Systems, Inc.
# All Rights Reserved.
#
# 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.
"""
Volume driver Test for Tegile storage.
"""
import mock
from cinder import context
from cinder.exception import TegileAPIException
from cinder import test
from cinder.volume.drivers import tegile
BASE_DRIVER = tegile.TegileIntelliFlashVolumeDriver
ISCSI_DRIVER = tegile.TegileISCSIDriver
FC_DRIVER = tegile.TegileFCDriver
test_config = mock.Mock()
test_config.san_ip = 'some-ip'
test_config.san_login = 'some-user'
test_config.san_password = 'some-password'
test_config.san_is_local = True
test_config.tegile_default_pool = 'random-pool'
test_config.tegile_default_project = 'random-project'
test_config.volume_backend_name = "unittest"
test_volume = {'host': 'node#testPool',
'name': 'testvol',
'id': 'a24c2ee8-525a-4406-8ccd-8d38688f8e9e',
'_name_id': 'testvol',
'metadata': {'project': 'testProj'},
'provider_location': None,
'size': 10}
test_snapshot = {'name': 'testSnap',
'id': '07ae9978-5445-405e-8881-28f2adfee732',
'volume': {'host': 'node#testPool',
'size': 1,
'_name_id': 'testvol'
}
}
array_stats = {'total_capacity_gb': 4569.199686084874,
'free_capacity_gb': 4565.381390112452,
'pools': [{'total_capacity_gb': 913.5,
'QoS_support': False,
'free_capacity_gb': 911.812650680542,
'reserved_percentage': 0,
'pool_name': 'pyramid'
},
{'total_capacity_gb': 2742.1996604874,
'QoS_support': False,
'free_capacity_gb': 2740.148867149747,
'reserved_percentage': 0,
'pool_name': 'cobalt'
},
{'total_capacity_gb': 913.5,
'QoS_support': False,
'free_capacity_gb': 913.4198722839355,
'reserved_percentage': 0,
'pool_name': 'test'
}]
}
class FakeTegileService(object):
@staticmethod
def send_api_request(method, params=None,
request_type='post',
api_service='v2',
fine_logging=False):
if method is 'createVolume':
return ''
elif method is 'deleteVolume':
return ''
elif method is 'createVolumeSnapshot':
return ''
elif method is 'deleteVolumeSnapshot':
return ''
elif method is 'cloneVolumeSnapshot':
return ''
elif method is 'listPools':
return ''
elif method is 'resizeVolume':
return ''
elif method is 'getVolumeSizeinGB':
return 25
elif method is 'getISCSIMappingForVolume':
return {'target_lun': '27',
'target_iqn': 'iqn.2012-02.com.tegile:openstack-cobalt',
'target_portal': '10.68.103.106:3260'
}
elif method is 'getFCPortsForVolume':
return {'target_lun': '12',
'initiator_target_map':
'{"21000024ff59bb6e":["21000024ff578701",],'
'"21000024ff59bb6f":["21000024ff578700",],}',
'target_wwn': '["21000024ff578700","21000024ff578701",]'}
elif method is 'getArrayStats':
return array_stats
fake_tegile_backend = FakeTegileService()
class FakeTegileServiceFail(object):
@staticmethod
def send_api_request(method, params=None,
request_type='post',
api_service='v2',
fine_logging=False):
raise TegileAPIException
fake_tegile_backend_fail = FakeTegileServiceFail()
class TegileIntelliFlashVolumeDriverTestCase(test.TestCase):
def setUp(self):
self.ctxt = context.get_admin_context()
self.configuration = test_config
super(TegileIntelliFlashVolumeDriverTestCase, self).setUp()
def test_create_volume(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({
'metadata': {'pool': 'testPool',
'project': test_config.tegile_default_project
}
}, tegile_driver.create_volume(test_volume))
def test_create_volume_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.create_volume,
test_volume)
def test_delete_volume(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
tegile_driver.delete_volume(test_volume)
def test_delete_volume_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.delete_volume,
test_volume)
def test_create_snapshot(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
tegile_driver.create_snapshot(test_snapshot)
def test_create_snapshot_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.create_snapshot,
test_snapshot)
def test_delete_snapshot(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
tegile_driver.delete_snapshot(test_snapshot)
def test_delete_snapshot_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.delete_snapshot,
test_snapshot)
def test_create_volume_from_snapshot(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({
'metadata': {'pool': 'testPool',
'project': test_config.tegile_default_project
}
}, tegile_driver.create_volume_from_snapshot(test_volume,
test_snapshot))
def test_create_volume_from_snapshot_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.create_volume_from_snapshot,
test_volume, test_snapshot)
def test_create_cloned_volume(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({'metadata': {'project': 'testProj',
'pool': 'testPool'}},
tegile_driver.create_cloned_volume(test_volume,
test_volume))
def test_create_cloned_volume_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.create_cloned_volume,
test_volume, test_volume)
def test_get_volume_stats(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({'driver_version': '1.0.0',
'free_capacity_gb': 4565.381390112452,
'pools': [{'QoS_support': False,
'allocated_capacity_gb': 0.0,
'free_capacity_gb': 911.812650680542,
'pool_name': 'pyramid',
'reserved_percentage': 0,
'total_capacity_gb': 913.5},
{'QoS_support': False,
'allocated_capacity_gb': 0.0,
'free_capacity_gb': 2740.148867149747,
'pool_name': 'cobalt',
'reserved_percentage': 0,
'total_capacity_gb': 2742.1996604874},
{'QoS_support': False,
'allocated_capacity_gb': 0.0,
'free_capacity_gb': 913.4198722839355,
'pool_name': 'test',
'reserved_percentage': 0,
'total_capacity_gb': 913.5}],
'storage_protocol': 'iSCSI',
'total_capacity_gb': 4569.199686084874,
'vendor_name': 'Tegile Systems Inc.',
'volume_backend_name': 'unittest'},
tegile_driver.get_volume_stats(True))
def test_get_pool(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual('testPool', tegile_driver.get_pool(test_volume))
def test_extend_volume(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
tegile_driver.extend_volume(test_volume, 12)
def test_extend_volume_fail(self):
tegile_driver = self.get_object(self.configuration)
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.extend_volume,
test_volume, 30)
def test_manage_existing(self):
tegile_driver = self.get_object(self.configuration)
existing_ref = {'name': 'existingvol'}
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({'metadata': {'pool': 'testPool',
'project': 'testProj'
},
'_name_id': ('existingvol',)
}, tegile_driver.manage_existing(test_volume,
existing_ref))
def test_manage_existing_get_size(self):
tegile_driver = self.get_object(self.configuration)
existing_ref = {'name': 'existingvol'}
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual(25,
tegile_driver.manage_existing_get_size(
test_volume,
existing_ref))
def test_manage_existing_get_size_fail(self):
tegile_driver = self.get_object(self.configuration)
existing_ref = {'name': 'existingvol'}
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend_fail):
self.assertRaises(TegileAPIException,
tegile_driver.manage_existing_get_size,
test_volume, existing_ref)
def get_object(self, configuration):
class TegileBaseDriver(BASE_DRIVER):
def initialize_connection(self, volume, connector, **kwargs):
pass
def terminate_connection(self, volume, connector,
force=False, **kwargs):
pass
return TegileBaseDriver(configuration=self.configuration)
class TegileISCSIDriverTestCase(test.TestCase):
def setUp(self):
super(TegileISCSIDriverTestCase, self).setUp()
self.ctxt = context.get_admin_context()
self.configuration = test_config
self.configuration.chap_username = 'fake'
self.configuration.chap_password = "test"
def test_initialize_connection(self):
tegile_driver = self.get_object(self.configuration)
connector = {'initiator': 'iqn.1993-08.org.debian:01:d0bb9a834f8'}
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual(
{'data': {'auth_method': 'CHAP',
'discard': False,
'target_discovered': (False,),
'auth_password': 'test',
'auth_username': 'fake',
'target_iqn': 'iqn.2012-02.'
'com.tegile:openstack-cobalt',
'target_lun': '27',
'target_portal': '10.68.103.106:3260',
'volume_id': (
'a24c2ee8-525a-4406-8ccd-8d38688f8e9e',)},
'driver_volume_type': 'iscsi'},
tegile_driver.initialize_connection(test_volume,
connector))
def get_object(self, configuration):
return ISCSI_DRIVER(configuration=configuration)
class TegileFCDriverTestCase(test.TestCase):
def setUp(self):
super(TegileFCDriverTestCase, self).setUp()
self.ctxt = context.get_admin_context()
self.configuration = test_config
def test_initialize_connection(self):
tegile_driver = self.get_object(self.configuration)
connector = {'wwpns': ['500110a0001a3990']}
with mock.patch.object(tegile_driver,
'_api_executor',
fake_tegile_backend):
self.assertEqual({'data': {'encrypted': False,
'initiator_target_map': {
'21000024ff59bb6e':
['21000024ff578701'],
'21000024ff59bb6f':
['21000024ff578700']
},
'target_discovered': False,
'target_lun': '12',
'target_wwn':
['21000024ff578700',
'21000024ff578701']},
'driver_volume_type': 'fibre_channel'},
tegile_driver.initialize_connection(
test_volume,
connector))
def get_object(self, configuration):
return FC_DRIVER(configuration=configuration)

View File

@ -0,0 +1,662 @@
# Copyright (c) 2015 by Tegile Systems, Inc.
# All Rights Reserved.
#
# 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.
"""
Volume driver for Tegile storage.
"""
import ast
import json
import requests
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 import utils
from cinder.i18n import _, _LI, _LW
from cinder import interface
from cinder.volume import driver
from cinder.volume.drivers.san import san
from cinder.volume import utils as volume_utils
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
default_api_service = 'openstack'
TEGILE_API_PATH = 'zebi/api'
TEGILE_DEFAULT_BLOCK_SIZE = '32KB'
TEGILE_LOCAL_CONTAINER_NAME = 'Local'
DEBUG_LOGGING = False
tegile_opts = [
cfg.StrOpt('tegile_default_pool',
help='Create volumes in this pool'),
cfg.StrOpt('tegile_default_project',
help='Create volumes in this project')]
CONF = cfg.CONF
CONF.register_opts(tegile_opts)
def debugger(func):
"""Returns a wrapper that wraps func.
The wrapper will log the entry and exit points of the function
"""
def wrapper(*args, **kwds):
if DEBUG_LOGGING:
LOG.debug('Entering %(classname)s.%(funcname)s',
{'classname': args[0].__class__.__name__,
'funcname': func.__name__})
LOG.debug('Arguments: %(args)s, %(kwds)s',
{'args': args[1:],
'kwds': kwds})
f_result = func(*args, **kwds)
if DEBUG_LOGGING:
LOG.debug('Exiting %(classname)s.%(funcname)s',
{'classname': args[0].__class__.__name__,
'funcname': func.__name__})
LOG.debug('Results: %(result)s',
{'result': f_result})
return f_result
return wrapper
class TegileAPIExecutor(object):
def __init__(self, classname, hostname, username, password):
self._classname = classname
self._hostname = hostname
self._username = username
self._password = password
@debugger
@utils.retry(exceptions=(requests.ConnectionError, requests.Timeout))
def send_api_request(self, method, params=None,
request_type='post',
api_service=default_api_service,
fine_logging=DEBUG_LOGGING):
if params is not None:
params = json.dumps(params)
url = 'https://%s/%s/%s/%s' % (self._hostname,
TEGILE_API_PATH,
api_service,
method)
if fine_logging:
LOG.debug('TegileAPIExecutor(%(classname)s) method: %(method)s, '
'url: %(url)s', {'classname': self._classname,
'method': method,
'url': url})
if request_type == 'post':
if fine_logging:
LOG.debug('TegileAPIExecutor(%(classname)s) '
'method: %(method)s, payload: %(payload)s',
{'classname': self._classname,
'method': method,
'payload': params})
req = requests.post(url,
data=params,
auth=(self._username, self._password),
verify=False)
else:
req = requests.get(url,
auth=(self._username, self._password),
verify=False)
if fine_logging:
LOG.debug('TegileAPIExecutor(%(classname)s) method: %(method)s, '
'return code: %(retcode)s',
{'classname': self._classname,
'method': method,
'retcode': req})
try:
response = req.json()
if fine_logging:
LOG.debug('TegileAPIExecutor(%(classname)s) '
'method: %(method)s, response: %(response)s',
{'classname': self._classname,
'method': method,
'response': response})
except ValueError:
response = ''
req.close()
if req.status_code != 200:
msg = _('API response: %(response)s') % {'response': response}
raise exception.TegileAPIException(msg)
return response
class TegileIntelliFlashVolumeDriver(san.SanDriver):
"""Tegile IntelliFlash Volume Driver."""
VENDOR = 'Tegile Systems Inc.'
VERSION = '1.0.0'
REQUIRED_OPTIONS = ['san_ip', 'san_login',
'san_password', 'tegile_default_pool']
SNAPSHOT_PREFIX = 'Manual-V-'
_api_executor = None
def __init__(self, *args, **kwargs):
self._context = None
super(TegileIntelliFlashVolumeDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(tegile_opts)
self._protocol = 'iSCSI' # defaults to iscsi
hostname = getattr(self.configuration, 'san_ip')
username = getattr(self.configuration, 'san_login')
password = getattr(self.configuration, 'san_password')
self._default_pool = getattr(self.configuration, 'tegile_default_pool')
self._default_project = (
getattr(self.configuration, 'tegile_default_project') or
'openstack')
self._api_executor = TegileAPIExecutor(self.__class__.__name__,
hostname,
username,
password)
@debugger
def do_setup(self, context):
super(TegileIntelliFlashVolumeDriver, self).do_setup(context)
self._context = context
self._check_ops(self.REQUIRED_OPTIONS, self.configuration)
@debugger
def create_volume(self, volume):
pool = volume_utils.extract_host(volume['host'], level='pool',
default_pool_name=self._default_pool)
tegile_volume = {'blockSize': TEGILE_DEFAULT_BLOCK_SIZE,
'datasetPath': '%s/%s/%s' %
(pool,
TEGILE_LOCAL_CONTAINER_NAME,
self._default_project),
'local': 'true',
'name': volume['name'],
'poolName': '%s' % pool,
'projectName': '%s' % self._default_project,
'protocol': self._protocol,
'thinProvision': 'true',
'volSize': volume['size'] * units.Gi}
params = list()
params.append(tegile_volume)
params.append(True)
self._api_executor.send_api_request(method='createVolume',
params=params)
LOG.info(_LI("Created volume %(volname)s, volume id %(volid)s."),
{'volname': volume['name'], 'volid': volume['id']})
return self.get_additional_info(volume, pool, self._default_project)
@debugger
def delete_volume(self, volume):
"""Deletes a snapshot."""
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
params.append(True)
params.append(False)
self._api_executor.send_api_request('deleteVolume', params)
@debugger
def create_snapshot(self, snapshot):
"""Creates a snapshot."""
snap_name = snapshot['name']
display_list = [getattr(snapshot, 'display_name', ''),
getattr(snapshot, 'display_description', '')]
snap_description = ':'.join(filter(None, display_list))
# Limit to 254 characters
snap_description = snap_description[:254]
pool, project, volume_name = self._get_pool_project_volume_name(
snapshot['volume'])
volume = {'blockSize': TEGILE_DEFAULT_BLOCK_SIZE,
'datasetPath': '%s/%s/%s' %
(pool,
TEGILE_LOCAL_CONTAINER_NAME,
project),
'local': 'true',
'name': volume_name,
'poolName': '%s' % pool,
'projectName': '%s' % project,
'protocol': self._protocol,
'thinProvision': 'true',
'volSize': snapshot['volume']['size'] * units.Gi}
params = list()
params.append(volume)
params.append(snap_name)
params.append(False)
LOG.info(_LI('Creating snapshot for volume_name=%(vol)s'
' snap_name=%(name)s snap_description=%(desc)s'),
{'vol': volume_name,
'name': snap_name,
'desc': snap_description})
self._api_executor.send_api_request('createVolumeSnapshot', params)
@debugger
def delete_snapshot(self, snapshot):
"""Deletes a snapshot."""
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(
snapshot['volume'])
params.append('%s/%s/%s/%s@%s%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name,
self.SNAPSHOT_PREFIX,
snapshot['name']))
params.append(False)
self._api_executor.send_api_request('deleteVolumeSnapshot', params)
@debugger
def create_volume_from_snapshot(self, volume, snapshot):
"""Creates a volume from snapshot."""
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(
snapshot['volume'])
params.append('%s/%s/%s/%s@%s%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name,
self.SNAPSHOT_PREFIX,
snapshot['name']))
params.append(volume['name'])
params.append(True)
params.append(True)
self._api_executor.send_api_request('cloneVolumeSnapshot', params)
return self.get_additional_info(volume, pool, project)
@debugger
def create_cloned_volume(self, volume, src_vref):
"""Creates a clone of the specified volume."""
pool, project, volume_name = self._get_pool_project_volume_name(
src_vref)
data_set_path = '%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project)
source_volume = {'blockSize': TEGILE_DEFAULT_BLOCK_SIZE,
'datasetPath': data_set_path,
'local': 'true',
'name': volume_name,
'poolName': '%s' % pool,
'projectName': '%s' % project,
'protocol': self._protocol,
'thinProvision': 'true',
'volSize': src_vref['size'] * units.Gi}
dest_volume = {'blockSize': TEGILE_DEFAULT_BLOCK_SIZE,
'datasetPath': data_set_path,
# clone can reside only in the source project
'local': 'true',
'name': volume['name'],
'poolName': '%s' % pool,
'projectName': '%s' % project,
'protocol': self._protocol,
'thinProvision': 'true',
'volSize': volume['size'] * units.Gi}
params = list()
params.append(source_volume)
params.append(dest_volume)
self._api_executor.send_api_request(method='createClonedVolume',
params=params)
return self.get_additional_info(volume, pool, project)
@debugger
def get_volume_stats(self, refresh=False):
"""Get volume status.
If 'refresh' is True, run update first.
The name is a bit misleading as
the majority of the data here is cluster
data
"""
if refresh:
try:
self._update_volume_stats()
except Exception:
pass
return self._stats
@debugger
def _update_volume_stats(self):
"""Retrieves stats info from volume group."""
try:
data = self._api_executor.send_api_request(method='getArrayStats',
request_type='get',
fine_logging=False)
# fixing values coming back here as String to float
data['total_capacity_gb'] = float(data.get('total_capacity_gb', 0))
data['free_capacity_gb'] = float(data.get('free_capacity_gb', 0))
for pool in data.get('pools', []):
pool['total_capacity_gb'] = float(
pool.get('total_capacity_gb', 0))
pool['free_capacity_gb'] = float(
pool.get('free_capacity_gb', 0))
pool['allocated_capacity_gb'] = float(
pool.get('allocated_capacity_gb', 0))
data['volume_backend_name'] = getattr(self.configuration,
'volume_backend_name')
data['vendor_name'] = self.VENDOR
data['driver_version'] = self.VERSION
data['storage_protocol'] = self._protocol
self._stats = data
except Exception as e:
LOG.warning(_LW('TegileIntelliFlashVolumeDriver(%(clsname)s) '
'_update_volume_stats failed: %(error)s'),
{'clsname': self.__class__.__name__,
'error': e})
@debugger
def get_pool(self, volume):
"""Returns pool name where volume resides.
:param volume: The volume hosted by the driver.
:return: Name of the pool where given volume is hosted.
"""
pool = volume_utils.extract_host(volume['host'], level='pool',
default_pool_name=self._default_pool)
return pool
@debugger
def extend_volume(self, volume, new_size):
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
vol_size = six.text_type(new_size)
params.append(vol_size)
params.append('GB')
self._api_executor.send_api_request(method='resizeVolume',
params=params)
@debugger
def manage_existing(self, volume, existing_ref):
volume['name_id'] = existing_ref['name']
pool, project, volume_name = self._get_pool_project_volume_name(volume)
additional_info = self.get_additional_info(volume, pool, project)
additional_info['_name_id'] = existing_ref['name'],
return additional_info
@debugger
def manage_existing_get_size(self, volume, existing_ref):
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
existing_ref['name']))
volume_size = self._api_executor.send_api_request(
method='getVolumeSizeinGB',
params=params)
return volume_size
@debugger
def _get_pool_project_volume_name(self, volume):
pool = volume_utils.extract_host(volume['host'], level='pool',
default_pool_name=self._default_pool)
try:
project = volume['metadata']['project']
except (AttributeError, TypeError, KeyError):
project = self._default_project
if volume['_name_id'] is not None:
volume_name = volume['_name_id']
else:
volume_name = volume['name']
return pool, project, volume_name
@debugger
def get_additional_info(self, volume, pool, project):
try:
metadata = self._get_volume_metadata(volume)
except Exception:
metadata = dict()
metadata['pool'] = pool
metadata['project'] = project
return {'metadata': metadata}
@debugger
def _get_volume_metadata(self, volume):
volume_metadata = {}
if 'volume_metadata' in volume:
for metadata in volume['volume_metadata']:
volume_metadata[metadata['key']] = metadata['value']
if 'metadata' in volume:
metadata = volume['metadata']
for key in metadata:
volume_metadata[key] = metadata[key]
return volume_metadata
@debugger
def _check_ops(self, required_ops, configuration):
"""Ensures that the options we care about are set."""
for attr in required_ops:
if not getattr(configuration, attr, None):
raise exception.InvalidInput(reason=_('%(attr)s is not '
'set.') % {'attr': attr})
@interface.volumedriver
class TegileISCSIDriver(TegileIntelliFlashVolumeDriver, san.SanISCSIDriver):
"""Tegile ISCSI Driver."""
def __init__(self, *args, **kwargs):
super(TegileISCSIDriver, self).__init__(*args, **kwargs)
self._protocol = 'iSCSI'
@debugger
def do_setup(self, context):
super(TegileISCSIDriver, self).do_setup(context)
@debugger
def initialize_connection(self, volume, connector):
"""Driver entry point to attach a volume to an instance."""
if getattr(self.configuration, 'use_chap_auth', False):
chap_username = getattr(self.configuration,
'chap_username',
'')
chap_password = getattr(self.configuration,
'chap_password',
'')
else:
chap_username = ''
chap_password = ''
if volume['provider_location'] is None:
params = list()
pool, project, volume_name = (
self._get_pool_project_volume_name(volume))
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
initiator_info = {
'initiatorName': connector['initiator'],
'chapUserName': chap_username,
'chapSecret': chap_password
}
params.append(initiator_info)
mapping_info = self._api_executor.send_api_request(
method='getISCSIMappingForVolume',
params=params)
target_portal = mapping_info['target_portal']
target_iqn = mapping_info['target_iqn']
target_lun = mapping_info['target_lun']
else:
(target_portal, target_iqn, target_lun) = (
volume['provider_location'].split())
connection_data = dict()
connection_data['target_portal'] = target_portal
connection_data['target_iqn'] = target_iqn
connection_data['target_lun'] = target_lun
connection_data['target_discovered'] = False,
connection_data['volume_id'] = volume['id'],
connection_data['discard'] = False
if getattr(self.configuration, 'use_chap_auth', False):
connection_data['auth_method'] = 'CHAP'
connection_data['auth_username'] = chap_username
connection_data['auth_password'] = chap_password
return {
'driver_volume_type': 'iscsi',
'data': connection_data
}
@debugger
def terminate_connection(self, volume, connector, **kwargs):
pass
@debugger
def create_export(self, context, volume, connector):
"""Driver entry point to get the export info for a new volume."""
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
if getattr(self.configuration, 'use_chap_auth', False):
chap_username = getattr(self.configuration, 'chap_username', '')
chap_password = getattr(self.configuration, 'chap_password', '')
else:
chap_username = ''
chap_password = ''
initiator_info = {
'initiatorName': connector['initiator'],
'chapUserName': chap_username,
'chapSecret': chap_password
}
params.append(initiator_info)
mapping_info = self._api_executor.send_api_request(
method='getISCSIMappingForVolume',
params=params)
target_portal = mapping_info['target_portal']
target_iqn = mapping_info['target_iqn']
target_lun = mapping_info['target_lun']
provider_location = '%s %s %s' % (target_portal,
target_iqn,
target_lun)
if getattr(self.configuration, 'use_chap_auth', False):
provider_auth = ('CHAP %s %s' % (chap_username,
chap_password))
else:
provider_auth = None
return (
{'provider_location': provider_location,
'provider_auth': provider_auth})
@interface.volumedriver
class TegileFCDriver(TegileIntelliFlashVolumeDriver,
driver.FibreChannelDriver):
"""Tegile FC driver."""
def __init__(self, *args, **kwargs):
super(TegileFCDriver, self).__init__(*args, **kwargs)
self._protocol = 'FC'
@debugger
def do_setup(self, context):
super(TegileFCDriver, self).do_setup(context)
@fczm_utils.AddFCZone
@debugger
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info."""
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
wwpns = connector['wwpns']
connectors = ','.join(wwpns)
params.append(connectors)
target_info = self._api_executor.send_api_request(
method='getFCPortsForVolume',
params=params)
initiator_target_map = target_info['initiator_target_map']
connection_data = {
'driver_volume_type': 'fibre_channel',
'data': {
'encrypted': False,
'target_discovered': False,
'target_lun': target_info['target_lun'],
'target_wwn': ast.literal_eval(target_info['target_wwn']),
'initiator_target_map': ast.literal_eval(initiator_target_map)
}
}
return connection_data
@fczm_utils.RemoveFCZone
@debugger
def terminate_connection(self, volume, connector, force=False, **kwargs):
params = list()
pool, project, volume_name = self._get_pool_project_volume_name(volume)
params.append('%s/%s/%s/%s' % (pool,
TEGILE_LOCAL_CONTAINER_NAME,
project,
volume_name))
wwpns = connector['wwpns']
connectors = ','.join(wwpns)
params.append(connectors)
target_info = self._api_executor.send_api_request(
method='getFCPortsForVolume',
params=params)
initiator_target_map = target_info['initiator_target_map']
connection_data = {
'data': {
'target_wwn': ast.literal_eval(target_info['target_wwn']),
'initiator_target_map': ast.literal_eval(initiator_target_map)
}
}
return connection_data

View File

@ -1,4 +0,0 @@
---
upgrade:
- Backend driver for Tegile has been removed.