From ee898c56b43d6ee0c5b5e6c53e3f11258dee9f07 Mon Sep 17 00:00:00 2001 From: Victor Rodionov Date: Tue, 5 Nov 2013 14:59:42 -0800 Subject: [PATCH] Nexenta iSCSI driver: extend volume stats of _update_volume_stats Added location_info to stats provided by _update_volume_stats method of Nexenta iSCSI volume driver. Change-Id: Ib947180cb6d08d7639443a8998190ab911d1a4a4 --- cinder/tests/test_nexenta.py | 53 ++++++++++++ cinder/units.py | 1 + cinder/volume/drivers/nexenta/iscsi.py | 113 ++++++++++++------------- cinder/volume/drivers/nexenta/utils.py | 8 ++ 4 files changed, 118 insertions(+), 57 deletions(-) diff --git a/cinder/tests/test_nexenta.py b/cinder/tests/test_nexenta.py index 5b5f3cb6c..5854b4cb9 100644 --- a/cinder/tests/test_nexenta.py +++ b/cinder/tests/test_nexenta.py @@ -33,6 +33,7 @@ from cinder.volume.drivers import nexenta from cinder.volume.drivers.nexenta import iscsi from cinder.volume.drivers.nexenta import jsonrpc from cinder.volume.drivers.nexenta import nfs +from cinder.volume.drivers.nexenta import utils class TestNexentaISCSIDriver(test.TestCase): @@ -677,3 +678,55 @@ class TestNexentaNfsDriver(test.TestCase): 'provider_location': self.TEST_EXPORT1 }) self.mox.ResetAll() + + +class TestNexentaUtils(test.TestCase): + + def test_str2size(self): + values_to_test = ( + # Test empty value + (None, 0), + ('', 0), + ('0', 0), + ('12', 12), + # Test int and long values + (10, 10), + (long(10), 10), + # Test bytes string + ('1b', 1), + ('1B', 1), + ('1023b', 1023), + ('0B', 0), + # Test other units + ('1M', units.MiB), + ('1.0M', units.MiB), + ) + + for value, result in values_to_test: + self.assertEquals(utils.str2size(value), result) + + # Invalid format value + self.assertRaises(ValueError, utils.str2size, 'A') + + def test_str2gib_size(self): + self.assertEqual(utils.str2gib_size('1024M'), 1) + self.assertEqual(utils.str2gib_size('300M'), + 300 * units.MiB // units.GiB) + self.assertEqual(utils.str2gib_size('1.2T'), + 1.2 * units.TiB // units.GiB) + self.assertRaises(ValueError, utils.str2gib_size, 'A') + + def test_parse_nms_url(self): + urls = ( + ('auto://192.168.1.1/', (True, 'http', 'admin', 'nexenta', + '192.168.1.1', '2000', '/rest/nms/')), + ('http://192.168.1.1/', (False, 'http', 'admin', 'nexenta', + '192.168.1.1', '2000', '/rest/nms/')), + ('http://192.168.1.1:8080', (False, 'http', 'admin', 'nexenta', + '192.168.1.1', '8080', '/rest/nms/')), + ('https://root:password@192.168.1.1:8080', + (False, 'https', 'root', 'password', '192.168.1.1', '8080', + '/rest/nms/')), + ) + for url, result in urls: + self.assertEquals(utils.parse_nms_url(url), result) diff --git a/cinder/units.py b/cinder/units.py index d0003e226..794347662 100644 --- a/cinder/units.py +++ b/cinder/units.py @@ -20,3 +20,4 @@ A module where we define some basic units for use across Cinder. KiB = 1024 MiB = KiB * 1024 GiB = MiB * 1024 +TiB = GiB * 1024 diff --git a/cinder/volume/drivers/nexenta/iscsi.py b/cinder/volume/drivers/nexenta/iscsi.py index 460a05479..47eddddf3 100644 --- a/cinder/volume/drivers/nexenta/iscsi.py +++ b/cinder/volume/drivers/nexenta/iscsi.py @@ -28,12 +28,13 @@ from oslo.config import cfg from cinder import exception from cinder.openstack.common import log as logging -from cinder import units from cinder.volume import driver from cinder.volume.drivers import nexenta from cinder.volume.drivers.nexenta import jsonrpc from cinder.volume.drivers.nexenta import options +from cinder.volume.drivers.nexenta import utils +VERSION = '1.1.3' LOG = logging.getLogger(__name__) CONF = cfg.CONF @@ -53,9 +54,10 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 1.1.1 - Ignore "does not exist" exception of nms.snapshot.destroy. 1.1.2 - Optimized create_cloned_volume, replaced zfs send recv with zfs clone. + 1.1.3 - Extended volume stats provided by _update_volume_stats method. """ - VERSION = '1.1.2' + VERSION = VERSION def __init__(self, *args, **kwargs): super(NexentaISCSIDriver, self).__init__(*args, **kwargs) @@ -67,31 +69,43 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 options.NEXENTA_ISCSI_OPTIONS) self.configuration.append_config_values( options.NEXENTA_VOLUME_OPTIONS) + self.nms_protocol = self.configuration.nexenta_rest_protocol + self.nms_host = self.configuration.nexenta_host + self.nms_port = self.configuration.nexenta_rest_port + self.nms_user = self.configuration.nexenta_user + self.nms_password = self.configuration.nexenta_password + self.volume = self.configuration.nexenta_volume + + @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): - protocol = self.configuration.nexenta_rest_protocol - auto = protocol == 'auto' - if auto: - protocol = 'http' + if self.nms_protocol == 'auto': + protocol, auto = 'http', True + else: + protocol, auto = self.nms_protocol, False self.nms = jsonrpc.NexentaJSONProxy( - protocol, self.configuration.nexenta_host, - self.configuration.nexenta_rest_port, '/rest/nms', - self.configuration.nexenta_user, - self.configuration.nexenta_password, auto=auto) + protocol, self.nms_host, self.nms_port, '/rest/nms', self.nms_user, + self.nms_password, auto=auto) def check_for_setup_error(self): """Verify that the volume for our zvols exists. :raise: :py:exc:`LookupError` """ - if not self.nms.volume.object_exists( - self.configuration.nexenta_volume): + if not self.nms.volume.object_exists(self.volume): raise LookupError(_("Volume %s does not exist in Nexenta SA"), - self.configuration.nexenta_volume) + self.volume) def _get_zvol_name(self, volume_name): """Return zvol name that corresponds given volume name.""" - return '%s/%s' % (self.configuration.nexenta_volume, volume_name) + return '%s/%s' % (self.volume, volume_name) def _get_target_name(self, volume_name): """Return iSCSI target name to access volume.""" @@ -102,11 +116,13 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 return '%s%s' % (self.configuration.nexenta_target_group_prefix, volume_name) - def _get_clone_snapshot_name(self, volume): + @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 _is_clone_snapshot_name(self, snapshot): + @staticmethod + def _is_clone_snapshot_name(snapshot): """Check if snapshot is created for cloning.""" name = snapshot.split('@')[-1] return name.startswith('cinder-clone-snapshot-') @@ -319,7 +335,7 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 def _get_provider_location(self, volume): """Returns volume iscsiadm-formatted provider location string.""" return '%(host)s:%(port)s,1 %(name)s 0' % { - 'host': self.configuration.nexenta_host, + 'host': self.nms_host, 'port': self.configuration.nexenta_iscsi_target_portal_port, 'name': self._get_target_name(volume['name']) } @@ -439,46 +455,29 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 return self._stats def _update_volume_stats(self): - """Retrieve stats info for Nexenta device.""" - - # NOTE(jdg): Aimon Bustardo was kind enough to point out the - # info he had regarding Nexenta Capabilities, ideally it would - # be great if somebody from Nexenta looked this over at some point - - LOG.debug(_("Updating volume stats")) - data = {} - backend_name = self.__class__.__name__ - if self.configuration: - backend_name = self.configuration.safe_get('volume_backend_name') - data["volume_backend_name"] = backend_name or self.__class__.__name__ - data["vendor_name"] = 'Nexenta' - data["driver_version"] = self.VERSION - data["storage_protocol"] = 'iSCSI' + """Retrieve stats info for NexentaStor appliance.""" + LOG.debug(_('Updating volume stats')) stats = self.nms.volume.get_child_props( self.configuration.nexenta_volume, 'health|size|used|available') - total_unit = stats['size'][-1] - total_amount = float(stats['size'][:-1]) - free_unit = stats['available'][-1] - free_amount = float(stats['available'][:-1]) - - if total_unit == "T": - total_amount *= units.KiB - elif total_unit == "M": - total_amount /= units.KiB - elif total_unit == "B": - total_amount /= units.MiB - - if free_unit == "T": - free_amount *= units.KiB - elif free_unit == "M": - free_amount /= units.KiB - elif free_unit == "B": - free_amount /= units.MiB - - data['total_capacity_gb'] = total_amount - data['free_capacity_gb'] = free_amount - - data['reserved_percentage'] = 0 - data['QoS_support'] = False - self._stats = data + + total_amount = utils.str2gib_size(stats['size']) + free_amount = utils.str2gib_size(stats['available']) + + location_info = '%(driver)s:%(host)s:%(volume)s' % { + 'driver': self.__class__.__name__, + 'host': self.nms_host, + 'volume': self.volume + } + + self._stats = { + 'vendor_name': 'Nexenta', + 'driver_version': self.VERSION, + 'storage_protocol': 'iSCSI', + 'total_capacity_gb': total_amount, + 'free_capacity_gb': free_amount, + 'reserved_percentage': 0, + 'QoS_support': False, + 'volume_backend_name': self.backend_name, + 'location_info': location_info + } diff --git a/cinder/volume/drivers/nexenta/utils.py b/cinder/volume/drivers/nexenta/utils.py index 03b2cd385..0bf75aa55 100644 --- a/cinder/volume/drivers/nexenta/utils.py +++ b/cinder/volume/drivers/nexenta/utils.py @@ -26,6 +26,8 @@ import re import urlparse +from cinder import units + def str2size(s, scale=1024): """Convert size-string. @@ -55,6 +57,12 @@ def str2size(s, scale=1024): return int(value * pow(scale, i)) +def str2gib_size(s): + """Covert size-string to size in gigabytes.""" + size_in_bytes = str2size(s) + return size_in_bytes / units.GiB + + def parse_nms_url(url): """Parse NMS url into normalized parts like scheme, user, host and others. -- 2.45.2