Nexenta: Added HA support and enhance get_volume_stats()
Added HA support in NexentaEdge iSCSI driver get_volume_stats function now returns total and free capacity Change-Id: I7a27d94fef9309ebd1bb82dbb2c518f917ab32ca
This commit is contained in:
parent
483bc008f3
commit
672120b372
@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from cinder import context
|
||||
@ -56,8 +57,14 @@ 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
|
||||
@ -82,13 +89,82 @@ class TestNexentaEdgeISCSIDriver(test.TestCase):
|
||||
self.mock_api.return_value = {
|
||||
'data': {'value': ISCSI_TARGET_STATUS}
|
||||
}
|
||||
self.driver.do_setup(context.get_admin_context())
|
||||
self.driver.do_setup(self.context)
|
||||
|
||||
self.addCleanup(self.api_patcher.stop)
|
||||
|
||||
def test_check_do_setup(self):
|
||||
self.assertEqual(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):
|
||||
if args[0] == 'service/isc/iscsi/status':
|
||||
return {'data': {'value': ISCSI_TARGET_STATUS}}
|
||||
else:
|
||||
return {'data': {'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)
|
||||
|
||||
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):
|
||||
if args[0] == 'service/isc/iscsi/status':
|
||||
return {'data': {'value': ISCSI_TARGET_STATUS}}
|
||||
else:
|
||||
return {'data': {'X-VIPS': json.dumps(vips)}}
|
||||
|
||||
self.mock_api.side_effect = my_side_effect
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.do_setup, self.context)
|
||||
|
||||
def test_check_do_setup__vip_no_client_address(self):
|
||||
self.cfg.nexenta_client_address = None
|
||||
first_vip = '1.2.3.4/32'
|
||||
vips = [
|
||||
[{'ip': first_vip}]
|
||||
]
|
||||
|
||||
def my_side_effect(*args, **kwargs):
|
||||
if args[0] == 'service/isc/iscsi/status':
|
||||
return {'data': {'value': ISCSI_TARGET_STATUS}}
|
||||
else:
|
||||
return {'data': {'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)
|
||||
|
||||
def test_check_do_setup__vip_no_client_address_2_xvips(self):
|
||||
self.cfg.nexenta_client_address = None
|
||||
first_vip = '1.2.3.4/32'
|
||||
vips = [
|
||||
[{'ip': first_vip}],
|
||||
[{'ip': '0.0.0.1/32'}]
|
||||
]
|
||||
|
||||
def my_side_effect(*args, **kwargs):
|
||||
if args[0] == 'service/isc/iscsi/status':
|
||||
return {'data': {'value': ISCSI_TARGET_STATUS}}
|
||||
else:
|
||||
return {'data': {'X-VIPS': json.dumps(vips)}}
|
||||
|
||||
self.mock_api.side_effect = my_side_effect
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.do_setup, self.context)
|
||||
|
||||
def test_create_volume(self):
|
||||
self.driver.create_volume(MOCK_VOL)
|
||||
self.mock_api.assert_called_with(NEDGE_URL, {
|
||||
@ -98,6 +174,17 @@ class TestNexentaEdgeISCSIDriver(test.TestCase):
|
||||
'chunkSize': NEDGE_CHUNKSIZE
|
||||
})
|
||||
|
||||
def test_create_volume__vip(self):
|
||||
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'
|
||||
})
|
||||
|
||||
def test_create_volume_fail(self):
|
||||
self.mock_api.side_effect = RuntimeError
|
||||
self.assertRaises(RuntimeError, self.driver.create_volume, MOCK_VOL)
|
||||
|
@ -25,6 +25,7 @@ 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__)
|
||||
@ -37,9 +38,10 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
Version history:
|
||||
1.0.0 - Initial driver version.
|
||||
1.0.1 - Moved opts to options.py.
|
||||
1.0.2 - Added HA support.
|
||||
"""
|
||||
|
||||
VERSION = '1.0.1'
|
||||
VERSION = '1.0.2'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NexentaEdgeISCSIDriver, self).__init__(*args, **kwargs)
|
||||
@ -67,6 +69,7 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
self.iscsi_target_port = (self.configuration.
|
||||
nexenta_iscsi_target_portal_port)
|
||||
self.target_vip = None
|
||||
self.ha_vip = None
|
||||
|
||||
@property
|
||||
def backend_name(self):
|
||||
@ -78,6 +81,13 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
return backend_name
|
||||
|
||||
def do_setup(self, context):
|
||||
def get_ip(host):
|
||||
hm = host[0 if len(host) == 1 else 1]['ip'].split('/', 1)
|
||||
return {
|
||||
'ip': hm[0],
|
||||
'mask': hm[1] if len(hm) > 1 else '32'
|
||||
}
|
||||
|
||||
if self.restapi_protocol == 'auto':
|
||||
protocol, auto = 'http', True
|
||||
else:
|
||||
@ -93,22 +103,37 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
data_keys = rsp['data'][list(rsp['data'].keys())[0]]
|
||||
self.target_name = data_keys.split('\n', 1)[0].split(' ')[2]
|
||||
|
||||
target_vip = self.configuration.safe_get(
|
||||
'nexenta_client_address')
|
||||
rsp = self.restapi.get('service/' + self.iscsi_service)
|
||||
if 'X-VIPS' in rsp['data']:
|
||||
vips = json.loads(rsp['data']['X-VIPS'])
|
||||
if len(vips[0]) == 1:
|
||||
self.target_vip = vips[0][0]['ip'].split('/', 1)[0]
|
||||
vips = [get_ip(host) for host in vips]
|
||||
if target_vip:
|
||||
found = False
|
||||
for host in vips:
|
||||
if target_vip == host['ip']:
|
||||
self.ha_vip = '/'.join((host['ip'], host['mask']))
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=_("nexenta_client_address doesn't match "
|
||||
"any VIPs provided by service: {}"
|
||||
).format(
|
||||
", ".join([host['ip'] for host in vips])))
|
||||
else:
|
||||
self.target_vip = vips[0][1]['ip'].split('/', 1)[0]
|
||||
else:
|
||||
self.target_vip = self.configuration.safe_get(
|
||||
'nexenta_client_address')
|
||||
if not self.target_vip:
|
||||
LOG.error(_LE('No VIP configured for service %s'),
|
||||
self.iscsi_service)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
_('No service VIP configured and '
|
||||
'no nexenta_client_address'))
|
||||
if len(vips) == 1:
|
||||
target_vip = vips[0]['ip']
|
||||
self.ha_vip = '/'.join(
|
||||
(vips[0]['ip'], vips[0]['mask']))
|
||||
if not target_vip:
|
||||
LOG.error(_LE('No VIP configured for service %s'),
|
||||
self.iscsi_service)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
message=_('No service VIP configured and '
|
||||
'no nexenta_client_address'))
|
||||
self.target_vip = target_vip
|
||||
except exception.VolumeBackendAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE('Error verifying iSCSI service %(serv)s on '
|
||||
@ -149,13 +174,16 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
}
|
||||
|
||||
def create_volume(self, volume):
|
||||
data = {
|
||||
'objectPath': self.bucket_path + '/' + volume['name'],
|
||||
'volSizeMB': int(volume['size']) * units.Ki,
|
||||
'blockSize': self.blocksize,
|
||||
'chunkSize': self.chunksize
|
||||
}
|
||||
if self.ha_vip:
|
||||
data['vip'] = self.ha_vip
|
||||
try:
|
||||
self.restapi.post('service/' + self.iscsi_service + '/iscsi', {
|
||||
'objectPath': self.bucket_path + '/' + volume['name'],
|
||||
'volSizeMB': int(volume['size']) * units.Ki,
|
||||
'blockSize': self.blocksize,
|
||||
'chunkSize': self.chunksize
|
||||
})
|
||||
self.restapi.post('service/' + self.iscsi_service + '/iscsi', data)
|
||||
except exception.VolumeBackendAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE('Error creating volume'))
|
||||
@ -239,8 +267,7 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
except exception.VolumeBackendAPIException:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE('Error creating cloned volume'))
|
||||
|
||||
if (('size' in volume) and (volume['size'] > src_vref['size'])):
|
||||
if volume['size'] > src_vref['size']:
|
||||
self.extend_volume(volume, volume['size'])
|
||||
|
||||
def create_export(self, context, volume, connector=None):
|
||||
@ -256,6 +283,11 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
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._get_target_address(None),
|
||||
@ -266,8 +298,8 @@ class NexentaEdgeISCSIDriver(driver.ISCSIDriver):
|
||||
'driver_version': self.VERSION,
|
||||
'storage_protocol': 'iSCSI',
|
||||
'reserved_percentage': 0,
|
||||
'total_capacity_gb': 'unknown',
|
||||
'free_capacity_gb': 'unknown',
|
||||
'total_capacity_gb': total,
|
||||
'free_capacity_gb': free,
|
||||
'QoS_support': False,
|
||||
'volume_backend_name': self.backend_name,
|
||||
'location_info': location_info,
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added HA support for NexentaEdge iSCSI driver
|
Loading…
Reference in New Issue
Block a user