def _create_host(self, port_id, host_type="linux"): """Creates host on system with given initiator as port_id.""" LOG.info(_("Creating host with port %s."), port_id) label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) port_label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) host_type = self._get_host_type_definition(host_type) return self._client.create_host_with_port(label, host_type, port_id, port_label)
def _create_host(self, port_id, host_type='linux'): """Creates host on system with given initiator as port_id.""" LOG.info(_("Creating host with port %s."), port_id) label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) port_label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) host_type = self._get_host_type_definition(host_type) return self._client.create_host_with_port(label, host_type, port_id, port_label)
def _get_latest_volume(self, uid): label = utils.convert_uuid_to_es_fmt(uid) for vol in self._client.list_volumes(): if vol.get('label') == label: self._cache_volume(vol) return self._get_cached_volume(label) raise exception.NetAppDriverException(_("Volume %s not found."), uid)
def _get_volume(self, uid): label = utils.convert_uuid_to_es_fmt(uid) try: return self._get_cached_volume(label) except KeyError: for vol in self._client.list_volumes(): if vol.get('label') == label: self._cache_volume(vol) break return self._get_cached_volume(label)
def _create_snapshot_volume(self, snapshot_id): """Creates snapshot volume for given group with snapshot_id.""" group = self._get_cached_snapshot_grp(snapshot_id) LOG.debug("Creating snap vol for group %s", group["label"]) image = self._get_cached_snap_grp_image(snapshot_id) label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) capacity = int(image["pitCapacity"]) / units.Gi storage_pools = self._get_sorted_avl_storage_pools(capacity) s_id = storage_pools[0]["volumeGroupRef"] return self._client.create_snapshot_volume(image["pitRef"], label, group["baseVolume"], s_id)
def _create_snapshot_volume(self, snapshot_id): """Creates snapshot volume for given group with snapshot_id.""" group = self._get_cached_snapshot_grp(snapshot_id) LOG.debug("Creating snap vol for group %s", group['label']) image = self._get_cached_snap_grp_image(snapshot_id) label = utils.convert_uuid_to_es_fmt(uuid.uuid4()) capacity = int(image['pitCapacity']) / units.Gi storage_pools = self._get_sorted_avl_storage_pools(capacity) s_id = storage_pools[0]['volumeGroupRef'] return self._client.create_snapshot_volume(image['pitRef'], label, group['baseVolume'], s_id)
def create_snapshot(self, snapshot): """Creates a snapshot.""" snap_grp, snap_image = None, None snapshot_name = utils.convert_uuid_to_es_fmt(snapshot["id"]) vol = self._get_volume(snapshot["volume_id"]) vol_size_gb = int(vol["totalSizeInBytes"]) / units.Gi pools = self._get_sorted_avl_storage_pools(vol_size_gb) try: snap_grp = self._client.create_snapshot_group(snapshot_name, vol["volumeRef"], pools[0]["volumeGroupRef"]) self._cache_snap_grp(snap_grp) snap_image = self._client.create_snapshot_image(snap_grp["pitGroupRef"]) self._cache_snap_img(snap_image) LOG.info(_("Created snap grp with label %s."), snapshot_name) except exception.NetAppDriverException: with excutils.save_and_reraise_exception(): if snap_image is None and snap_grp: self.delete_snapshot(snapshot)
def create_volume(self, volume): """Creates a volume.""" LOG.debug("create_volume on %s" % volume["host"]) # get E-series pool label as pool name eseries_pool_label = volume_utils.extract_host(volume["host"], level="pool") if eseries_pool_label is None: msg = _("Pool is not available in the volume host field.") raise exception.InvalidHost(reason=msg) eseries_volume_label = utils.convert_uuid_to_es_fmt(volume["id"]) # get size of the requested volume creation size_gb = int(volume["size"]) vol = self._create_volume(eseries_pool_label, eseries_volume_label, size_gb) self._cache_volume(vol)
def create_snapshot(self, snapshot): """Creates a snapshot.""" snap_grp, snap_image = None, None snapshot_name = utils.convert_uuid_to_es_fmt(snapshot['id']) vol = self._get_volume(snapshot['volume_id']) vol_size_gb = int(vol['totalSizeInBytes']) / units.Gi pools = self._get_sorted_avl_storage_pools(vol_size_gb) try: snap_grp = self._client.create_snapshot_group( snapshot_name, vol['volumeRef'], pools[0]['volumeGroupRef']) self._cache_snap_grp(snap_grp) snap_image = self._client.create_snapshot_image( snap_grp['pitGroupRef']) self._cache_snap_img(snap_image) LOG.info(_("Created snap grp with label %s."), snapshot_name) except exception.NetAppDriverException: with excutils.save_and_reraise_exception(): if snap_image is None and snap_grp: self.delete_snapshot(snapshot)
def create_volume(self, volume): """Creates a volume.""" LOG.debug('create_volume on %s' % volume['host']) # get E-series pool label as pool name eseries_pool_label = volume_utils.extract_host(volume['host'], level='pool') if eseries_pool_label is None: msg = _("Pool is not available in the volume host field.") raise exception.InvalidHost(reason=msg) eseries_volume_label = utils.convert_uuid_to_es_fmt(volume['id']) # get size of the requested volume creation size_gb = int(volume['size']) vol = self._create_volume(eseries_pool_label, eseries_volume_label, size_gb) self._cache_volume(vol)
def create_volume_from_snapshot(self, volume, snapshot): """Creates a volume from a snapshot.""" label = utils.convert_uuid_to_es_fmt(volume['id']) size = volume['size'] dst_vol = self._schedule_and_create_volume(label, size) try: src_vol = None src_vol = self._create_snapshot_volume(snapshot['id']) self._copy_volume_high_prior_readonly(src_vol, dst_vol) self._cache_volume(dst_vol) LOG.info(_("Created volume with label %s."), label) except exception.NetAppDriverException: with excutils.save_and_reraise_exception(): self._client.delete_volume(dst_vol['volumeRef']) finally: if src_vol: try: self._client.delete_snapshot_volume(src_vol['id']) except exception.NetAppDriverException as e: LOG.error(_("Failure deleting snap vol. Error: %s."), e) else: LOG.warn(_("Snapshot volume not found."))
def extend_volume(self, volume, new_size): """Extend an existing volume to the new size.""" stage_1, stage_2 = 0, 0 src_vol = self._get_volume(volume['id']) src_label = src_vol['label'] stage_label = 'tmp-%s' % utils.convert_uuid_to_es_fmt(uuid.uuid4()) extend_vol = {'id': uuid.uuid4(), 'size': new_size} self.create_cloned_volume(extend_vol, volume) new_vol = self._get_volume(extend_vol['id']) try: stage_1 = self._client.update_volume(src_vol['id'], stage_label) stage_2 = self._client.update_volume(new_vol['id'], src_label) new_vol = stage_2 self._cache_volume(new_vol) self._cache_volume(stage_1) LOG.info(_('Extended volume with label %s.'), src_label) except exception.NetAppDriverException: if stage_1 == 0: with excutils.save_and_reraise_exception(): self._client.delete_volume(new_vol['id']) if stage_2 == 0: with excutils.save_and_reraise_exception(): self._client.update_volume(src_vol['id'], src_label) self._client.delete_volume(new_vol['id'])
def test_convert_uuid_to_es_fmt(self): value = 'e67e931a-b2ed-4890-938b-3acc6a517fac' result = na_utils.convert_uuid_to_es_fmt(value) self.assertEqual(result, '4Z7JGGVS5VEJBE4LHLGGUUL7VQ')
def _get_cached_snapshot_grp(self, uid): label = utils.convert_uuid_to_es_fmt(uid) snap_id = self._objects['snapshots']['label_ref'][label] return self._objects['snapshots']['ref_snap'][snap_id]
def create_volume(self, volume): """Creates a volume.""" label = utils.convert_uuid_to_es_fmt(volume['id']) size_gb = int(volume['size']) vol = self._create_volume(label, size_gb) self._cache_volume(vol)
def _get_volume(self, uid): label = utils.convert_uuid_to_es_fmt(uid) try: return self._get_cached_volume(label) except KeyError: return self._get_latest_volume(uid)
class NetAppEseriesIscsiDriverTestCase(test.TestCase): """Test case for NetApp e-series iscsi driver.""" volume = {'id': '114774fb-e15a-4fae-8ee2-c9723e3645ef', 'size': 1, 'volume_name': 'lun1', 'host': 'hostname@backend#DDP', 'os_type': 'linux', 'provider_location': 'lun1', 'id': '114774fb-e15a-4fae-8ee2-c9723e3645ef', 'provider_auth': 'provider a b', 'project_id': 'project', 'display_name': None, 'display_description': 'lun1', 'volume_type_id': None} snapshot = {'id': '17928122-553b-4da9-9737-e5c3dcd97f75', 'volume_id': '114774fb-e15a-4fae-8ee2-c9723e3645ef', 'size': 2, 'volume_name': 'lun1', 'volume_size': 2, 'project_id': 'project', 'display_name': None, 'display_description': 'lun1', 'volume_type_id': None} volume_sec = {'id': 'b6c01641-8955-4917-a5e3-077147478575', 'size': 2, 'volume_name': 'lun1', 'os_type': 'linux', 'provider_location': 'lun1', 'id': 'b6c01641-8955-4917-a5e3-077147478575', 'provider_auth': None, 'project_id': 'project', 'display_name': None, 'display_description': 'lun1', 'volume_type_id': None} volume_clone = {'id': 'b4b24b27-c716-4647-b66d-8b93ead770a5', 'size': 3, 'volume_name': 'lun1', 'os_type': 'linux', 'provider_location': 'cl_sm', 'id': 'b4b24b27-c716-4647-b66d-8b93ead770a5', 'provider_auth': None, 'project_id': 'project', 'display_name': None, 'display_description': 'lun1', 'volume_type_id': None} volume_clone_large = {'id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553', 'size': 6, 'volume_name': 'lun1', 'os_type': 'linux', 'provider_location': 'cl_lg', 'id': 'f6ef5bf5-e24f-4cbb-b4c4-11d631d6e553', 'provider_auth': None, 'project_id': 'project', 'display_name': None, 'display_description': 'lun1', 'volume_type_id': None} fake_eseries_volume_label = na_utils.convert_uuid_to_es_fmt(volume['id']) connector = {'initiator': 'iqn.1998-01.com.vmware:localhost-28a58148'} fake_size_gb = volume['size'] fake_eseries_pool_label = 'DDP' def setUp(self): super(NetAppEseriesIscsiDriverTestCase, self).setUp() self._custom_setup() def _custom_setup(self): configuration = self._set_config(create_configuration()) self.driver = common.NetAppDriver(configuration=configuration) self.mock_object(requests, 'Session', FakeEseriesHTTPSession) self.driver.do_setup(context='context') self.driver.check_for_setup_error() def _set_config(self, configuration): configuration.netapp_storage_family = 'eseries' configuration.netapp_storage_protocol = 'iscsi' configuration.netapp_transport_type = 'http' configuration.netapp_server_hostname = '127.0.0.1' configuration.netapp_server_port = '80' configuration.netapp_webservice_path = '/devmgr/vn' configuration.netapp_controller_ips = '127.0.0.2,127.0.0.3' configuration.netapp_sa_password = '******' configuration.netapp_login = '******' configuration.netapp_password = '******' configuration.netapp_storage_pools = 'DDP' return configuration def test_embedded_mode(self): configuration = self._set_config(create_configuration()) configuration.netapp_controller_ips = '127.0.0.1,127.0.0.3' driver = common.NetAppDriver(configuration=configuration) driver.do_setup(context='context') self.assertEqual(driver._client.get_system_id(), '1fa6efb5-f07b-4de4-9f0e-52e5f7ff5d1b') def test_check_system_pwd_not_sync(self): def list_system(): if getattr(self, 'test_count', None): self.test_count = 1 return {'status': 'passwordoutofsync'} return {'status': 'needsAttention'} self.driver._client.list_storage_system = mock.Mock(wraps=list_system) result = self.driver._check_storage_system() self.assertTrue(result) def test_connect(self): self.driver.check_for_setup_error() def test_create_destroy(self): self.driver.create_volume(self.volume) self.driver.delete_volume(self.volume) def test_create_vol_snapshot_destroy(self): self.driver.create_volume(self.volume) self.driver.create_snapshot(self.snapshot) self.driver.create_volume_from_snapshot(self.volume_sec, self.snapshot) self.driver.delete_snapshot(self.snapshot) self.driver.delete_volume(self.volume) def test_map_unmap(self): self.driver.create_volume(self.volume) connection_info = self.driver.initialize_connection(self.volume, self.connector) self.assertEqual(connection_info['driver_volume_type'], 'iscsi') properties = connection_info.get('data') self.assertIsNotNone(properties, 'Target portal is none') self.driver.terminate_connection(self.volume, self.connector) self.driver.delete_volume(self.volume) def test_map_already_mapped_same_host(self): self.driver.create_volume(self.volume) maps = [{'lunMappingRef': 'hdkjsdhjsdh', 'mapRef': '8400000060080E500023C73400300381515BFBA3', 'volumeRef': '0200000060080E500023BB34000003FB515C2293', 'lun': 2}] self.driver._get_host_mapping_for_vol_frm_array = mock.Mock( return_value=maps) self.driver._get_free_lun = mock.Mock() info = self.driver.initialize_connection(self.volume, self.connector) self.assertEqual( self.driver._get_host_mapping_for_vol_frm_array.call_count, 1) self.assertEqual(self.driver._get_free_lun.call_count, 0) self.assertEqual(info['driver_volume_type'], 'iscsi') properties = info.get('data') self.assertIsNotNone(properties, 'Target portal is none') self.driver.terminate_connection(self.volume, self.connector) self.driver.delete_volume(self.volume) def test_map_already_mapped_diff_host(self): self.driver.create_volume(self.volume) maps = [{'lunMappingRef': 'hdkjsdhjsdh', 'mapRef': '7400000060080E500023C73400300381515BFBA3', 'volumeRef': 'CFDXJ67BLJH25DXCZFZD4NSF54', 'lun': 2}] self.driver._get_host_mapping_for_vol_frm_array = mock.Mock( return_value=maps) self.driver._get_vol_mapping_for_host_frm_array = mock.Mock( return_value=[]) self.driver._get_free_lun = mock.Mock(return_value=0) self.driver._del_vol_mapping_frm_cache = mock.Mock() info = self.driver.initialize_connection(self.volume, self.connector) self.assertEqual( self.driver._get_vol_mapping_for_host_frm_array.call_count, 1) self.assertEqual( self.driver._get_host_mapping_for_vol_frm_array.call_count, 1) self.assertEqual(self.driver._get_free_lun.call_count, 1) self.assertEqual(self.driver._del_vol_mapping_frm_cache.call_count, 1) self.assertEqual(info['driver_volume_type'], 'iscsi') properties = info.get('data') self.assertIsNotNone(properties, 'Target portal is none') self.driver.terminate_connection(self.volume, self.connector) self.driver.delete_volume(self.volume) def test_cloned_volume_destroy(self): self.driver.create_volume(self.volume) self.driver.create_cloned_volume(self.snapshot, self.volume) self.driver.delete_volume(self.volume) def test_map_by_creating_host(self): self.driver.create_volume(self.volume) connector_new = {'initiator': 'iqn.1993-08.org.debian:01:1001'} connection_info = self.driver.initialize_connection(self.volume, connector_new) self.assertEqual(connection_info['driver_volume_type'], 'iscsi') properties = connection_info.get('data') self.assertIsNotNone(properties, 'Target portal is none') def test_vol_stats(self): self.driver.get_volume_stats(refresh=True) def test_create_vol_snapshot_diff_size_resize(self): self.driver.create_volume(self.volume) self.driver.create_snapshot(self.snapshot) self.driver.create_volume_from_snapshot( self.volume_clone, self.snapshot) self.driver.delete_snapshot(self.snapshot) self.driver.delete_volume(self.volume) def test_create_vol_snapshot_diff_size_subclone(self): self.driver.create_volume(self.volume) self.driver.create_snapshot(self.snapshot) self.driver.create_volume_from_snapshot( self.volume_clone_large, self.snapshot) self.driver.delete_snapshot(self.snapshot) self.driver.delete_volume(self.volume) @mock.patch.object(iscsi.Driver, '_get_volume', mock.Mock(return_value={'volumeGroupRef': 'fake_ref'})) def test_get_pool(self): self.driver._objects['pools'] = [{'volumeGroupRef': 'fake_ref', 'label': 'ddp1'}] pool = self.driver.get_pool({'id': 'fake-uuid'}) self.assertEqual(pool, 'ddp1') @mock.patch.object(iscsi.Driver, '_get_volume', mock.Mock(return_value={'volumeGroupRef': 'fake_ref'})) def test_get_pool_no_pools(self): self.driver._objects['pools'] = [] pool = self.driver.get_pool({'id': 'fake-uuid'}) self.assertEqual(pool, None) @mock.patch.object(iscsi.Driver, '_get_volume', mock.Mock(return_value={'volumeGroupRef': 'fake_ref'})) def test_get_pool_no_match(self): self.driver._objects['pools'] = [{'volumeGroupRef': 'fake_ref2', 'label': 'ddp2'}] pool = self.driver.get_pool({'id': 'fake-uuid'}) self.assertEqual(pool, None) @mock.patch.object(iscsi.Driver, '_create_volume', mock.Mock()) def test_create_volume(self): self.driver.create_volume(self.volume) self.driver._create_volume.assert_called_with( 'DDP', self.fake_eseries_volume_label, self.volume['size']) def test_create_volume_no_pool_provided_by_scheduler(self): volume = copy.deepcopy(self.volume) volume['host'] = "host@backend" # missing pool self.assertRaises(exception.InvalidHost, self.driver.create_volume, volume) @mock.patch.object(client.RestClient, 'list_storage_pools') def test_helper_create_volume_fail(self, fake_list_pools): fake_pool = {} fake_pool['label'] = self.fake_eseries_pool_label fake_pool['volumeGroupRef'] = 'foo' fake_pools = [fake_pool] fake_list_pools.return_value = fake_pools wrong_eseries_pool_label = 'hostname@backend' self.assertRaises(exception.NetAppDriverException, self.driver._create_volume, wrong_eseries_pool_label, self.fake_eseries_volume_label, self.fake_size_gb) @mock.patch.object(driver_log, 'info') @mock.patch.object(client.RestClient, 'list_storage_pools') @mock.patch.object(client.RestClient, 'create_volume', mock.MagicMock(return_value='CorrectVolume')) def test_helper_create_volume(self, storage_pools, log_info): fake_pool = {} fake_pool['label'] = self.fake_eseries_pool_label fake_pool['volumeGroupRef'] = 'foo' fake_pools = [fake_pool] storage_pools.return_value = fake_pools drv = self.driver storage_vol = drv.driver._create_volume(self.fake_eseries_pool_label, self.fake_eseries_volume_label, self.fake_size_gb) log_info.assert_called_once_with("Created volume with label %s.", self.fake_eseries_volume_label) self.assertEqual('CorrectVolume', storage_vol) @mock.patch.object(client.RestClient, 'list_storage_pools') @mock.patch.object(client.RestClient, 'create_volume', mock.MagicMock( side_effect=exception.NetAppDriverException)) @mock.patch.object(driver_log, 'info', mock.Mock()) def test_create_volume_check_exception(self, fake_list_pools): fake_pool = {} fake_pool['label'] = self.fake_eseries_pool_label fake_pool['volumeGroupRef'] = 'foo' fake_pools = [fake_pool] fake_list_pools.return_value = fake_pools self.assertRaises(exception.NetAppDriverException, self.driver._create_volume, self.fake_eseries_pool_label, self.fake_eseries_volume_label, self.fake_size_gb) def test_portal_for_vol_controller(self): volume = {'id': 'vol_id', 'currentManager': 'ctrl1'} vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'} portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'}, {'controller': 'ctrl1', 'iqn': 'iqn1'}] portal = self.driver._get_iscsi_portal_for_vol(volume, portals) self.assertEqual(portal, {'controller': 'ctrl1', 'iqn': 'iqn1'}) portal = self.driver._get_iscsi_portal_for_vol(vol_nomatch, portals) self.assertEqual(portal, {'controller': 'ctrl2', 'iqn': 'iqn2'}) def test_portal_for_vol_any_false(self): vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'} portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'}, {'controller': 'ctrl1', 'iqn': 'iqn1'}] self.assertRaises(exception.NetAppDriverException, self.driver._get_iscsi_portal_for_vol, vol_nomatch, portals, False)
def _get_cached_snapshot_grp(self, uid): label = utils.convert_uuid_to_es_fmt(uid) snap_id = self._objects["snapshots"]["label_ref"][label] return self._objects["snapshots"]["ref_snap"][snap_id]