Remove the Nexenta Edge Driver
The Nexenta Edge driver was marked unsupported during the Stein release. The 3rd Party CI has not reported in 57 days. This continues to fall outside our 3rd Party CI Policy. This patch proposes removal of the driver. Change-Id: I219b5e5bfa4f77afc895ec9c219e61e3fdb1b762
This commit is contained in:
parent
9aca21f5ce
commit
a1c58b50ea
@ -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)
|
@ -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
|
||||
}
|
@ -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')
|
@ -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
|
@ -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
|
@ -82,3 +82,4 @@ release.
|
||||
* Train
|
||||
* Tintri Storage Driver
|
||||
* Veritas HyperScale Storage Driver
|
||||
* Nexenta Edge Storage Driver
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user