diff --git a/cinder/tests/unit/volume/drivers/nexenta/test_nexenta_edge.py b/cinder/tests/unit/volume/drivers/nexenta/test_nexenta_edge.py deleted file mode 100644 index 8460b7ea8cd..00000000000 --- a/cinder/tests/unit/volume/drivers/nexenta/test_nexenta_edge.py +++ /dev/null @@ -1,269 +0,0 @@ -# -# Copyright 2015 Nexenta 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. - -import json -import mock -from mock import patch - -from cinder import context -from cinder import exception -from cinder import test -from cinder.volume import configuration as conf -from cinder.volume.drivers.nexenta.nexentaedge import iscsi -from cinder.volume.drivers.nexenta import utils - -NEDGE_BUCKET = 'c/t/bk' -NEDGE_SERVICE = 'isc' -NEDGE_URL = 'service/%s/iscsi' % NEDGE_SERVICE -NEDGE_BLOCKSIZE = 4096 -NEDGE_CHUNKSIZE = 16384 - -MOCK_VOL = { - 'id': 'vol1', - 'name': 'vol1', - 'size': 1 -} -MOCK_VOL2 = { - 'id': 'vol2', - 'name': 'vol2', - 'size': 1 -} -MOCK_VOL3 = { - 'id': 'vol3', - 'name': 'vol3', - 'size': 2 -} -MOCK_SNAP = { - 'id': 'snap1', - 'name': 'snap1', - 'volume_name': 'vol1', - 'volume_size': 1 -} -NEW_VOL_SIZE = 2 -ISCSI_TARGET_NAME = 'iscsi_target_name:' -ISCSI_TARGET_STATUS = 'Target 1: ' + ISCSI_TARGET_NAME - - -class TestNexentaEdgeISCSIDriver(test.TestCase): - - def setUp(self): - def _safe_get(opt): - return getattr(self.cfg, opt) - super(TestNexentaEdgeISCSIDriver, self).setUp() - self.context = context.get_admin_context() - self.cfg = mock.Mock(spec=conf.Configuration) - self.cfg.safe_get = mock.Mock(side_effect=_safe_get) - self.cfg.trace_flags = 'fake_trace_flags' - self.cfg.driver_data_namespace = 'fake_driver_data_namespace' - self.cfg.nexenta_client_address = '0.0.0.0' - self.cfg.nexenta_rest_address = '0.0.0.0' - self.cfg.nexenta_rest_port = 8080 - self.cfg.nexenta_rest_protocol = 'http' - self.cfg.nexenta_iscsi_target_portal_port = 3260 - self.cfg.nexenta_rest_user = 'admin' - self.cfg.driver_ssl_cert_verify = False - self.cfg.nexenta_rest_password = 'admin' - self.cfg.nexenta_lun_container = NEDGE_BUCKET - self.cfg.nexenta_iscsi_service = NEDGE_SERVICE - self.cfg.nexenta_blocksize = NEDGE_BLOCKSIZE - self.cfg.nexenta_chunksize = NEDGE_CHUNKSIZE - self.cfg.nexenta_replication_count = 2 - self.cfg.nexenta_encryption = True - self.cfg.replication_device = None - self.cfg.nexenta_iops_limit = 0 - - mock_exec = mock.Mock() - mock_exec.return_value = ('', '') - self.driver = iscsi.NexentaEdgeISCSIDriver(execute=mock_exec, - configuration=self.cfg) - self.api_patcher = mock.patch('cinder.volume.drivers.nexenta.' - 'nexentaedge.jsonrpc.' - 'NexentaEdgeJSONProxy.__call__') - self.mock_api = self.api_patcher.start() - - self.mock_api.return_value = { - 'data': { - 'X-ISCSI-TargetName': ISCSI_TARGET_NAME, - 'X-ISCSI-TargetID': 1} - } - self.driver.do_setup(self.context) - - self.addCleanup(self.api_patcher.stop) - - def test_check_do_setup(self): - self.assertEqual('%s1' % ISCSI_TARGET_NAME, self.driver.target_name) - - def test_check_do_setup__vip(self): - first_vip = '/'.join((self.cfg.nexenta_client_address, '32')) - vips = [ - [{'ip': first_vip}], - [{'ip': '0.0.0.1/32'}] - ] - - def my_side_effect(*args, **kwargs): - return {'data': { - 'X-ISCSI-TargetName': ISCSI_TARGET_NAME, - 'X-ISCSI-TargetID': 1, - 'X-VIPS': json.dumps(vips)} - } - - self.mock_api.side_effect = my_side_effect - self.driver.do_setup(self.context) - self.assertEqual(self.driver.ha_vip, first_vip.split('/')[0]) - - def test_check_do_setup__vip_not_in_xvips(self): - first_vip = '1.2.3.4/32' - vips = [ - [{'ip': first_vip}], - [{'ip': '0.0.0.1/32'}] - ] - - def my_side_effect(*args, **kwargs): - return {'data': { - 'X-ISCSI-TargetName': ISCSI_TARGET_NAME, - 'X-ISCSI-TargetID': 1, - 'X-VIPS': json.dumps(vips)} - } - - self.mock_api.side_effect = my_side_effect - self.assertRaises(utils.NexentaException, - self.driver.do_setup, self.context) - - def check_for_setup_error(self): - self.mock_api.side_effect = exception.VolumeBackendAPIException - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - - @patch('cinder.volume.drivers.nexenta.nexentaedge.iscsi.' - 'NexentaEdgeISCSIDriver._get_lu_number') - def test_create_volume(self, lun): - lun.return_value = 1 - self.driver.create_volume(MOCK_VOL) - - self.mock_api.assert_called_with(NEDGE_URL, { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'volSizeMB': MOCK_VOL['size'] * 1024, - 'blockSize': NEDGE_BLOCKSIZE, - 'chunkSize': NEDGE_CHUNKSIZE, - 'optionsObject': { - 'ccow-replication-count': 2, - 'ccow-encryption-enabled': True, - 'ccow-iops-rate-lim': 0} - }) - - @patch('cinder.volume.drivers.nexenta.nexentaedge.iscsi.' - 'NexentaEdgeISCSIDriver._get_lu_number') - def test_create_volume__vip(self, lun): - lun.return_value = 1 - self.driver.ha_vip = self.cfg.nexenta_client_address + '/32' - self.driver.create_volume(MOCK_VOL) - self.mock_api.assert_called_with(NEDGE_URL, { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'volSizeMB': MOCK_VOL['size'] * 1024, - 'blockSize': NEDGE_BLOCKSIZE, - 'chunkSize': NEDGE_CHUNKSIZE, - 'vip': self.cfg.nexenta_client_address + '/32', - 'optionsObject': { - 'ccow-replication-count': 2, - 'ccow-encryption-enabled': True, - 'ccow-iops-rate-lim': 0} - }) - - def test_create_volume_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.create_volume, MOCK_VOL) - - def test_delete_volume(self): - self.mock_api.side_effect = exception.VolumeBackendAPIException( - 'No volume') - self.driver.delete_volume(MOCK_VOL) - self.mock_api.assert_called_with(NEDGE_URL, { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'] - }) - - def test_delete_volume_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.delete_volume, MOCK_VOL) - - def test_extend_volume(self): - self.driver.extend_volume(MOCK_VOL, NEW_VOL_SIZE) - self.mock_api.assert_called_with(NEDGE_URL + '/resize', { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'newSizeMB': NEW_VOL_SIZE * 1024 - }) - - def test_extend_volume_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.extend_volume, - MOCK_VOL, NEW_VOL_SIZE) - - def test_create_snapshot(self): - self.driver.create_snapshot(MOCK_SNAP) - self.mock_api.assert_called_with(NEDGE_URL + '/snapshot', { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'snapName': MOCK_SNAP['id'] - }) - - def test_create_snapshot_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.create_snapshot, MOCK_SNAP) - - def test_delete_snapshot(self): - self.driver.delete_snapshot(MOCK_SNAP) - self.mock_api.assert_called_with(NEDGE_URL + '/snapshot', { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'snapName': MOCK_SNAP['id'] - }) - - def test_delete_snapshot_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.delete_snapshot, MOCK_SNAP) - - def test_create_volume_from_snapshot(self): - self.driver.create_volume_from_snapshot(MOCK_VOL2, MOCK_SNAP) - self.mock_api.assert_called_with(NEDGE_URL + '/snapshot/clone', { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_SNAP['volume_name'], - 'clonePath': NEDGE_BUCKET + '/' + MOCK_VOL2['id'], - 'snapName': MOCK_SNAP['id'] - }) - - def test_create_volume_from_snapshot_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, - self.driver.create_volume_from_snapshot, - MOCK_VOL2, MOCK_SNAP) - - def test_create_cloned_volume(self): - self.driver.create_cloned_volume(MOCK_VOL2, MOCK_VOL) - url = '%s/snapshot/clone' % NEDGE_URL - self.mock_api.assert_called_with(url, { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL['id'], - 'clonePath': NEDGE_BUCKET + '/' + MOCK_VOL2['id'], - 'snapName': 'cinder-clone-snapshot-vol2' - }) - - def test_create_cloned_volume_larger(self): - self.driver.create_cloned_volume(MOCK_VOL3, MOCK_VOL) - # ignore the clone call, this has been tested before - self.mock_api.assert_called_with(NEDGE_URL + '/resize', { - 'objectPath': NEDGE_BUCKET + '/' + MOCK_VOL3['id'], - 'newSizeMB': MOCK_VOL3['size'] * 1024 - }) - - def test_create_cloned_volume_fail(self): - self.mock_api.side_effect = RuntimeError - self.assertRaises(RuntimeError, self.driver.create_cloned_volume, - MOCK_VOL2, MOCK_VOL) diff --git a/cinder/volume/drivers/nexenta/nexentaedge/__init__.py b/cinder/volume/drivers/nexenta/nexentaedge/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py b/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py deleted file mode 100644 index 315317a8405..00000000000 --- a/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py +++ /dev/null @@ -1,345 +0,0 @@ -# Copyright 2015 Nexenta 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. - -from oslo_log import log as logging -from oslo_utils import excutils -from oslo_utils import units - -from cinder import exception -from cinder.i18n import _ -from cinder import interface -from cinder.volume import driver -from cinder.volume.drivers.nexenta.nexentaedge import jsonrpc -from cinder.volume.drivers.nexenta import options -from cinder.volume.drivers.nexenta import utils as nexenta_utils - - -LOG = logging.getLogger(__name__) - - -@interface.volumedriver -class NexentaEdgeISCSIDriver(driver.ISCSIDriver): - """Executes volume driver commands on NexentaEdge cluster. - - .. code-block:: none - - Version history: - - 1.0.0 - Initial driver version. - 1.0.1 - Moved opts to options.py. - 1.0.2 - Added HA support. - 1.0.3 - Added encryption and replication count support. - 1.0.4 - Added initialize_connection. - 1.0.5 - Driver re-introduced in OpenStack. - """ - - VERSION = '1.0.5' - - # ThirdPartySystems wiki page - CI_WIKI_NAME = "Nexenta_Edge_CI" - - # TODO(jsbryant) Remove driver in the 'T' release if CI is not fixed - SUPPORTED = False - - def __init__(self, *args, **kwargs): - super(NexentaEdgeISCSIDriver, self).__init__(*args, **kwargs) - if self.configuration: - self.configuration.append_config_values( - options.NEXENTA_CONNECTION_OPTS) - self.configuration.append_config_values( - options.NEXENTA_ISCSI_OPTS) - self.configuration.append_config_values( - options.NEXENTA_DATASET_OPTS) - self.configuration.append_config_values( - options.NEXENTA_EDGE_OPTS) - if self.configuration.nexenta_rest_address: - self.restapi_host = self.configuration.nexenta_rest_address - else: - self.restapi_host = self.configuration.san_ip - - if self.configuration.nexenta_rest_port: - self.restapi_port = self.configuration.nexenta_rest_port - else: - self.restapi_port = self.configuration.san_api_port - - if self.configuration.nexenta_client_address: - self.target_vip = self.configuration.nexenta_client_address - else: - self.target_vip = self.configuration.target_ip_address - if self.configuration.nexenta_rest_password: - self.restapi_password = ( - self.configuration.nexenta_rest_password) - else: - self.restapi_password = ( - self.configuration.san_password) - if self.configuration.nexenta_rest_user: - self.restapi_user = self.configuration.nexenta_rest_user - else: - self.restapi_user = self.configuration.san_login - self.verify_ssl = self.configuration.driver_ssl_cert_verify - self.restapi_protocol = self.configuration.nexenta_rest_protocol - self.iscsi_service = self.configuration.nexenta_iscsi_service - self.bucket_path = self.configuration.nexenta_lun_container - self.blocksize = self.configuration.nexenta_blocksize - self.chunksize = self.configuration.nexenta_chunksize - self.cluster, self.tenant, self.bucket = self.bucket_path.split('/') - self.repcount = self.configuration.nexenta_replication_count - self.encryption = self.configuration.nexenta_encryption - self.iscsi_target_port = (self.configuration. - nexenta_iscsi_target_portal_port) - self.ha_vip = None - - @staticmethod - def get_driver_options(): - return ( - options.NEXENTA_CONNECTION_OPTS + - options.NEXENTA_ISCSI_OPTS + - options.NEXENTA_DATASET_OPTS + - options.NEXENTA_EDGE_OPTS - ) - - @property - def backend_name(self): - backend_name = None - if self.configuration: - backend_name = self.configuration.safe_get('volume_backend_name') - if not backend_name: - backend_name = self.__class__.__name__ - return backend_name - - def do_setup(self, context): - if self.restapi_protocol == 'auto': - protocol, auto = 'http', True - else: - protocol, auto = self.restapi_protocol, False - - try: - self.restapi = jsonrpc.NexentaEdgeJSONProxy( - protocol, self.restapi_host, self.restapi_port, '/', - self.restapi_user, self.restapi_password, - self.verify_ssl, auto=auto) - - data = self.restapi.get('service/' + self.iscsi_service)['data'] - self.target_name = '%s%s' % ( - data['X-ISCSI-TargetName'], data['X-ISCSI-TargetID']) - if 'X-VIPS' in data: - if self.target_vip not in data['X-VIPS']: - raise nexenta_utils.NexentaException( - 'Configured client IP address does not match any VIP' - ' provided by iSCSI service %s' % self.iscsi_service) - else: - self.ha_vip = self.target_vip - except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - LOG.exception('Error verifying iSCSI service %(serv)s on ' - 'host %(hst)s', { - 'serv': self.iscsi_service, - 'hst': self.restapi_host}) - - def check_for_setup_error(self): - url = 'clusters/%s/tenants/%s/buckets' % (self.cluster, self.tenant) - if self.bucket not in self.restapi.get(url): - raise exception.VolumeBackendAPIException( - message=_('Bucket %s does not exist') % self.bucket) - - def _get_lu_number(self, volname): - rsp = self.restapi.get('service/' + self.iscsi_service + '/iscsi') - path = '%s/%s' % (self.bucket_path, volname) - for mapping in rsp['data']: - if mapping['objectPath'] == path: - return mapping['number'] - return None - - def _get_provider_location(self, volume): - lun = self._get_lu_number(volume['name']) - if not lun: - return None - return '%(host)s:%(port)s,1 %(name)s %(number)s' % { - 'host': self.target_vip, - 'port': self.iscsi_target_port, - 'name': self.target_name, - 'number': lun - } - - def create_volume(self, volume): - data = { - 'objectPath': '%s/%s' % ( - self.bucket_path, volume['name']), - 'volSizeMB': int(volume['size']) * units.Ki, - 'blockSize': self.blocksize, - 'chunkSize': self.chunksize, - 'optionsObject': { - 'ccow-replication-count': self.repcount, - 'ccow-iops-rate-lim': self.configuration.nexenta_iops_limit} - } - if self.encryption: - data['optionsObject']['ccow-encryption-enabled'] = True - if self.ha_vip: - data['vip'] = self.ha_vip - try: - self.restapi.post('service/' + self.iscsi_service + '/iscsi', data) - except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - LOG.exception( - 'Error creating LUN for volume %s', volume['name']) - return {'provider_location': self._get_provider_location(volume)} - - def delete_volume(self, volume): - data = { - 'objectPath': '%s/%s' % ( - self.bucket_path, volume['name']) - } - try: - self.restapi.delete( - 'service/' + self.iscsi_service + '/iscsi', data) - except exception.VolumeBackendAPIException: - LOG.info( - 'Error deleting LUN for volume %s', volume['name']) - - def create_export(self, context, volume, connector=None): - pass - - def ensure_export(self, context, volume): - pass - - def remove_export(self, context, volume): - pass - - def initialize_connection(self, volume, connector): - return { - 'driver_volume_type': 'iscsi', - 'data': { - 'target_discovered': False, - 'encrypted': False, - 'qos_specs': None, - 'target_iqn': self.target_name, - 'target_portal': '%s:%s' % ( - self.target_vip, self.iscsi_target_port), - 'volume_id': volume['id'], - 'target_lun': self._get_lu_number(volume['name']), - 'access_mode': 'rw', - } - } - - def extend_volume(self, volume, new_size): - try: - self.restapi.put('service/' + self.iscsi_service + '/iscsi/resize', - {'objectPath': '%s/%s' % ( - self.bucket_path, volume['name']), - 'newSizeMB': new_size * units.Ki}) - except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - LOG.exception('Error extending volume %s', volume['name']) - - def create_volume_from_snapshot(self, volume, snapshot): - try: - self.restapi.put( - 'service/' + self.iscsi_service + '/iscsi/snapshot/clone', - { - 'objectPath': '%s/%s' % ( - self.bucket_path, snapshot['volume_name']), - 'clonePath': '%s/%s' % ( - self.bucket_path, volume['name']), - 'snapName': snapshot['name'] - }) - except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - LOG.exception( - 'Error creating volume from snapshot %s', snapshot['name']) - if (('size' in volume) and ( - volume['size'] > snapshot['volume_size'])): - self.extend_volume(volume, volume['size']) - - def create_snapshot(self, snapshot): - try: - self.restapi.post( - 'service/' + self.iscsi_service + '/iscsi/snapshot', - { - 'objectPath': '%s/%s' % ( - self.bucket_path, snapshot['volume_name']), - 'snapName': snapshot['name'] - }) - except exception.VolumeBackendAPIException: - with excutils.save_and_reraise_exception(): - LOG.exception('Error creating snapshot %s', snapshot['name']) - - def delete_snapshot(self, snapshot): - try: - self.restapi.delete( - 'service/' + self.iscsi_service + '/iscsi/snapshot', - { - 'objectPath': '%s/%s' % ( - self.bucket_path, snapshot['volume_name']), - 'snapName': snapshot['name'] - }) - except exception.VolumeBackendAPIException: - LOG.info('Error deleting snapshot %s', snapshot['name']) - - @staticmethod - def _get_clone_snapshot_name(volume): - """Return name for snapshot that will be used to clone the volume.""" - return 'cinder-clone-snapshot-%(id)s' % volume - - def create_cloned_volume(self, volume, src_vref): - snapshot = {'volume_name': src_vref['name'], - 'volume_id': src_vref['id'], - 'volume_size': src_vref['size'], - 'name': self._get_clone_snapshot_name(volume)} - LOG.debug('Creating temp snapshot of the original volume: ' - '%s@%s', snapshot['volume_name'], snapshot['name']) - self.create_snapshot(snapshot) - try: - self.create_volume_from_snapshot(volume, snapshot) - except nexenta_utils.NexentaException: - LOG.error('Volume creation failed, deleting created snapshot ' - '%s', '@'.join([snapshot['volume_name'], - snapshot['name']])) - try: - self.delete_snapshot(snapshot) - except (nexenta_utils.NexentaException, exception.SnapshotIsBusy): - LOG.warning('Failed to delete zfs snapshot ' - '%s', '@'.join([snapshot['volume_name'], - snapshot['name']])) - raise - if volume['size'] > src_vref['size']: - self.extend_volume(volume, volume['size']) - - def local_path(self, volume): - raise NotImplementedError - - def get_volume_stats(self, refresh=False): - resp = self.restapi.get('system/stats') - summary = resp['stats']['summary'] - total = nexenta_utils.str2gib_size(summary['total_capacity']) - free = nexenta_utils.str2gib_size(summary['total_available']) - - location_info = '%(driver)s:%(host)s:%(bucket)s' % { - 'driver': self.__class__.__name__, - 'host': self.target_vip, - 'bucket': self.bucket_path - } - return { - 'vendor_name': 'Nexenta', - 'driver_version': self.VERSION, - 'storage_protocol': 'iSCSI', - 'reserved_percentage': 0, - 'total_capacity_gb': total, - 'free_capacity_gb': free, - 'QoS_support': False, - 'volume_backend_name': self.backend_name, - 'location_info': location_info, - 'iscsi_target_portal_port': self.iscsi_target_port, - 'restapi_url': self.restapi.url - } diff --git a/cinder/volume/drivers/nexenta/nexentaedge/jsonrpc.py b/cinder/volume/drivers/nexenta/nexentaedge/jsonrpc.py deleted file mode 100644 index 125904b5e18..00000000000 --- a/cinder/volume/drivers/nexenta/nexentaedge/jsonrpc.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright 2015 Nexenta 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. - -import json -import requests - -from oslo_log import log as logging - -from cinder import exception -from cinder.i18n import _ -from cinder.utils import retry - -LOG = logging.getLogger(__name__) -TIMEOUT = 60 - - -class NexentaEdgeJSONProxy(object): - - retry_exc_tuple = ( - requests.exceptions.ConnectionError, - requests.exceptions.ConnectTimeout - ) - - def __init__(self, protocol, host, port, path, user, password, verify, - auto=False, method=None, session=None): - if session: - self.session = session - else: - self.session = requests.Session() - self.session.auth = (user, password) - self.session.headers.update({'Content-Type': 'application/json'}) - self.protocol = protocol.lower() - self.verify = verify - self.host = host - self.port = port - self.path = path - self.user = user - self.password = password - self.auto = auto - self.method = method - - @property - def url(self): - return '%s://%s:%s/%s' % ( - self.protocol, self.host, self.port, self.path) - - def __getattr__(self, name): - if name in ('get', 'post', 'put', 'delete'): - return NexentaEdgeJSONProxy( - self.protocol, self.host, self.port, self.path, self.user, - self.password, self.verify, self.auto, name, self.session) - return super(NexentaEdgeJSONProxy, self).__getattr__(name) - - def __hash__(self): - return self.url.__hash__() - - def __repr__(self): - return 'HTTP JSON proxy: %s' % self.url - - @retry(retry_exc_tuple, interval=1, retries=6) - def __call__(self, *args): - self.path = args[0] - kwargs = {'timeout': TIMEOUT, 'verify': self.verify} - data = None - if len(args) > 1: - data = json.dumps(args[1]) - kwargs['data'] = data - - LOG.debug('Sending JSON data: %s, method: %s, data: %s', - self.url, self.method, data) - - func = getattr(self.session, self.method) - if func: - req = func(self.url, **kwargs) - else: - raise exception.VolumeDriverException( - message=_('Unsupported method: %s') % self.method) - - rsp = req.json() - - LOG.debug('Got response: %s', rsp) - if rsp.get('response') is None: - raise exception.VolumeBackendAPIException( - data=_('Error response: %s') % rsp) - return rsp.get('response') diff --git a/doc/source/configuration/block-storage/drivers/nexentaedge-driver.rst b/doc/source/configuration/block-storage/drivers/nexentaedge-driver.rst deleted file mode 100644 index 340e5930a1b..00000000000 --- a/doc/source/configuration/block-storage/drivers/nexentaedge-driver.rst +++ /dev/null @@ -1,89 +0,0 @@ -=============================== -NexentaEdge NBD & iSCSI drivers -=============================== - -NexentaEdge is designed from the ground-up to deliver high performance Block -and Object storage services and limitless scalability to next generation -OpenStack clouds, petabyte scale active archives and Big Data applications. -NexentaEdge runs on shared nothing clusters of industry standard Linux -servers, and builds on Nexenta IP and patent pending Cloud Copy On Write (CCOW) -technology to break new ground in terms of reliability, functionality and cost -efficiency. - -For NexentaEdge user documentation, visit https://nexentaedge.github.io. - - -iSCSI driver -~~~~~~~~~~~~ - -The NexentaEdge cluster must be installed and configured according to the -relevant Nexenta documentation. A cluster, tenant, bucket must be pre-created, -as well as an iSCSI service on the NexentaEdge gateway node. - -The NexentaEdge iSCSI driver is selected using the normal procedures for one -or multiple back-end volume drivers. - -You must configure these items for each NexentaEdge cluster that the iSCSI -volume driver controls: - -#. Make the following changes on the volume node ``/etc/cinder/cinder.conf`` - file. - - .. code-block:: ini - - # Enable Nexenta iSCSI driver - volume_driver = cinder.volume.drivers.nexenta.nexentaedge.iscsi.NexentaEdgeISCSIDriver - - # Specify the ip address for Rest API (string value) - nexenta_rest_address = MANAGEMENT-NODE-IP - - # Port for Rest API (integer value) - nexenta_rest_port=8080 - - # Protocol used for Rest calls (string value, default=htpp) - nexenta_rest_protocol = http - - # Username for NexentaEdge Rest (string value) - nexenta_rest_user=USERNAME - - # Password for NexentaEdge Rest (string value) - nexenta_rest_password=PASSWORD - - # Path to bucket containing iSCSI LUNs (string value) - nexenta_lun_container = CLUSTER/TENANT/BUCKET - - # Name of pre-created iSCSI service (string value) - nexenta_iscsi_service = SERVICE-NAME - - # IP address of the gateway node attached to iSCSI service above or - # virtual IP address if an iSCSI Storage Service Group is configured in - # HA mode (string value) - nexenta_client_address = GATEWAY-NODE-IP - - -#. Save the changes to the ``/etc/cinder/cinder.conf`` file and - restart the ``cinder-volume`` service. - -Supported operations --------------------- - -* Create, delete, attach, and detach volumes. - -* Create, list, and delete volume snapshots. - -* Create a volume from a snapshot. - -* Copy an image to a volume. - -* Copy a volume to an image. - -* Clone a volume. - -* Extend a volume. - -Driver options -~~~~~~~~~~~~~~ - -Nexenta Driver supports these options: - -.. include:: ../../tables/cinder-nexenta_edge.inc diff --git a/doc/source/configuration/tables/cinder-nexenta_edge.inc b/doc/source/configuration/tables/cinder-nexenta_edge.inc deleted file mode 100644 index 20e762bdccb..00000000000 --- a/doc/source/configuration/tables/cinder-nexenta_edge.inc +++ /dev/null @@ -1,46 +0,0 @@ -.. - Warning: Do not edit this file. It is automatically generated from the - software project's code and your changes will be overwritten. - - The tool to generate this file lives in openstack-doc-tools repository. - - Please make any changes needed in the code, then run the - autogenerate-config-doc tool from the openstack-doc-tools repository, or - ask for help on the documentation mailing list, IRC channel or meeting. - -.. _cinder-nexenta_edge: - -.. list-table:: Description of NexentaEdge driver configuration options - :header-rows: 1 - :class: config-ref-table - - * - Configuration option = Default value - - Description - * - **[DEFAULT]** - - - * - ``nexenta_blocksize`` = ``4096`` - - (Integer) Block size for datasets - * - ``nexenta_chunksize`` = ``32768`` - - (Integer) NexentaEdge iSCSI LUN object chunk size - * - ``nexenta_client_address`` = - - (String) NexentaEdge iSCSI Gateway client address for non-VIP service - * - ``nexenta_iscsi_service`` = - - (String) NexentaEdge iSCSI service name - * - ``nexenta_iscsi_target_portal_port`` = ``3260`` - - (Integer) Nexenta target portal port - * - ``nexenta_lun_container`` = - - (String) NexentaEdge logical path of bucket for LUNs - * - ``nexenta_rest_address`` = - - (String) IP address of NexentaEdge management REST API endpoint - * - ``nexenta_rest_password`` = ``nexenta`` - - (String) Password to connect to NexentaEdge - * - ``nexenta_rest_port`` = ``8080`` - - (Integer) HTTP port to connect to Nexenta REST API server - * - ``nexenta_rest_protocol`` = ``auto`` - - (String) Use http or https for REST connection (default auto) - * - ``nexenta_rest_user`` = ``admin`` - - (String) User name to connect to NexentaEdge - * - ``nexenta_replication_count`` = ``3`` - - (String) NexentaEdge iSCSI LUN object replication count. - * - ``nexenta_encryption`` = ``False`` - - (String) NexentaEdge iSCSI LUN object encryption diff --git a/doc/source/reference/support-matrix.rst b/doc/source/reference/support-matrix.rst index 3f1e77b7041..0caba131c20 100644 --- a/doc/source/reference/support-matrix.rst +++ b/doc/source/reference/support-matrix.rst @@ -82,3 +82,4 @@ release. * Train * Tintri Storage Driver * Veritas HyperScale Storage Driver + * Nexenta Edge Storage Driver diff --git a/releasenotes/notes/nexenta-edge-driver-removal-5626d542d75f3d43.yaml b/releasenotes/notes/nexenta-edge-driver-removal-5626d542d75f3d43.yaml new file mode 100644 index 00000000000..98fbf7fa886 --- /dev/null +++ b/releasenotes/notes/nexenta-edge-driver-removal-5626d542d75f3d43.yaml @@ -0,0 +1,15 @@ +--- +upgrade: + - | + The Nexenta Edge storage driver has been removed after completion of its + deprecation period without a reliable 3rd Party CI system being + supported. Customers using the Nexenta Edge driver should not upgrade + Cinder without first migrating all volumes from their Nexenta backend + to a supported storage backend. Failure to migrate volumes will + result in no longer being able to access volumes back by the Nexenta Edge + storage backend. +other: + - | + The Nexenta Edge storage driver was marked unsupported in Stein due to + 3rd Party CI not meeting Cinder's requirements. As a result the + driver is removed starting from the Train release.