Merge "Add get_manageable_* methods to Pure drivers"

This commit is contained in:
Jenkins 2016-08-28 19:35:38 +00:00 committed by Gerrit Code Review
commit 9fbc1e5c6f
3 changed files with 333 additions and 2 deletions

View File

@ -23,6 +23,7 @@ from oslo_utils import units
from cinder import exception
from cinder import test
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
@ -323,6 +324,108 @@ REPLICATED_VOL_TYPE = {"is_public": True,
"<is> True"},
"name": "volume_type_2",
"id": VOLUME_TYPE_ID}
MANAGEABLE_PURE_VOLS = [
{
'name': 'myVol1',
'serial': '8E9C7E588B16C1EA00048CCA',
'size': 3221225472,
'created': '2016-08-05T17:26:34Z',
'source': None,
},
{
'name': 'myVol2',
'serial': '8E9C7E588B16C1EA00048CCB',
'size': 3221225472,
'created': '2016-08-05T17:26:34Z',
'source': None,
},
{
'name': 'myVol3',
'serial': '8E9C7E588B16C1EA00048CCD',
'size': 3221225472,
'created': '2016-08-05T17:26:34Z',
'source': None,
}
]
MANAGEABLE_PURE_VOL_REFS = [
{
'reference': {'name': 'myVol1'},
'size': 3,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
},
{
'reference': {'name': 'myVol2'},
'size': 3,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
},
{
'reference': {'name': 'myVol3'},
'size': 3,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
}
]
MANAGEABLE_PURE_SNAPS = [
{
'name': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder.snap1',
'serial': '8E9C7E588B16C1EA00048CCA',
'size': 3221225472,
'created': '2016-08-05T17:26:34Z',
'source': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder',
},
{
'name': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder.snap2',
'serial': '8E9C7E588B16C1EA00048CCB',
'size': 4221225472,
'created': '2016-08-05T17:26:34Z',
'source': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder',
},
{
'name': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder.snap3',
'serial': '8E9C7E588B16C1EA00048CCD',
'size': 5221225472,
'created': '2016-08-05T17:26:34Z',
'source': 'volume-fd33de6e-56f6-452d-a7b6-451c11089a9f-cinder',
}
]
MANAGEABLE_PURE_SNAP_REFS = [
{
'reference': {'name': MANAGEABLE_PURE_SNAPS[0]['name']},
'size': 3,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
'source_reference': {'name': MANAGEABLE_PURE_SNAPS[0]['source']},
},
{
'reference': {'name': MANAGEABLE_PURE_SNAPS[1]['name']},
'size': 4,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
'source_reference': {'name': MANAGEABLE_PURE_SNAPS[1]['source']},
},
{
'reference': {'name': MANAGEABLE_PURE_SNAPS[2]['name']},
'size': 5,
'safe_to_manage': True,
'reason_not_safe': None,
'cinder_id': None,
'extra_info': None,
'source_reference': {'name': MANAGEABLE_PURE_SNAPS[2]['source']},
}
]
class FakePureStorageHTTPError(Exception):
@ -1552,6 +1655,136 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase):
self.assertEqual(expected, actual)
return context, volume
def _test_get_manageable_things(self,
pure_objs=MANAGEABLE_PURE_VOLS,
expected_refs=MANAGEABLE_PURE_VOL_REFS,
pure_hosts=list(),
cinder_objs=list(),
is_snapshot=False):
self.array.list_volumes.return_value = pure_objs
self.array.list_hosts.return_value = pure_hosts
marker = mock.Mock()
limit = mock.Mock()
offset = mock.Mock()
sort_keys = mock.Mock()
sort_dirs = mock.Mock()
with mock.patch('cinder.volume.utils.paginate_entries_list') as mpage:
if is_snapshot:
test_func = self.driver.get_manageable_snapshots
else:
test_func = self.driver.get_manageable_volumes
test_func(cinder_objs, marker, limit, offset, sort_keys, sort_dirs)
mpage.assert_called_once_with(
expected_refs,
marker,
limit,
offset,
sort_keys,
sort_dirs
)
def test_get_manageable_volumes(self,):
"""Default success case.
Given a list of pure volumes from the REST API, give back a list
of volume references.
"""
self._test_get_manageable_things(pure_hosts=[PURE_HOST])
def test_get_manageable_volumes_connected_vol(self):
"""Make sure volumes connected to hosts are flagged as unsafe."""
connected_host = deepcopy(PURE_HOST)
connected_host['name'] = 'host2'
connected_host['vol'] = MANAGEABLE_PURE_VOLS[0]['name']
pure_hosts = [PURE_HOST, connected_host]
expected_refs = deepcopy(MANAGEABLE_PURE_VOL_REFS)
expected_refs[0]['safe_to_manage'] = False
expected_refs[0]['reason_not_safe'] = 'Volume connected to host host2.'
self._test_get_manageable_things(expected_refs=expected_refs,
pure_hosts=pure_hosts)
def test_get_manageable_volumes_already_managed(self):
"""Make sure volumes already owned by cinder are flagged as unsafe."""
cinder_vol = fake_volume.fake_volume_obj(mock.MagicMock())
cinder_vol.id = VOLUME_ID
cinders_vols = [cinder_vol]
# Have one of our vol names match up with the existing cinder volume
purity_vols = deepcopy(MANAGEABLE_PURE_VOLS)
purity_vols[0]['name'] = 'volume-' + VOLUME_ID + '-cinder'
expected_refs = deepcopy(MANAGEABLE_PURE_VOL_REFS)
expected_refs[0]['reference'] = {'name': purity_vols[0]['name']}
expected_refs[0]['safe_to_manage'] = False
expected_refs[0]['reason_not_safe'] = 'Volume already managed.'
expected_refs[0]['cinder_id'] = VOLUME_ID
self._test_get_manageable_things(pure_objs=purity_vols,
expected_refs=expected_refs,
pure_hosts=[PURE_HOST],
cinder_objs=cinders_vols)
def test_get_manageable_volumes_no_pure_volumes(self):
"""Expect no refs to be found if no volumes are on Purity."""
self._test_get_manageable_things(pure_objs=[],
expected_refs=[],
pure_hosts=[PURE_HOST])
def test_get_manageable_volumes_no_hosts(self):
"""Success case with no hosts on Purity."""
self._test_get_manageable_things(pure_hosts=[])
def test_get_manageable_snapshots(self):
"""Default success case.
Given a list of pure snapshots from the REST API, give back a list
of snapshot references.
"""
self._test_get_manageable_things(
pure_objs=MANAGEABLE_PURE_SNAPS,
expected_refs=MANAGEABLE_PURE_SNAP_REFS,
pure_hosts=[PURE_HOST],
is_snapshot=True
)
def test_get_manageable_snapshots_already_managed(self):
"""Make sure snaps already owned by cinder are flagged as unsafe."""
cinder_vol = fake_volume.fake_volume_obj(mock.MagicMock())
cinder_vol.id = VOLUME_ID
cinder_snap = fake_snapshot.fake_snapshot_obj(mock.MagicMock())
cinder_snap.id = SNAPSHOT_ID
cinder_snap.volume = cinder_vol
cinder_snaps = [cinder_snap]
purity_snaps = deepcopy(MANAGEABLE_PURE_SNAPS)
purity_snaps[0]['name'] = 'volume-%s-cinder.snapshot-%s' % (
VOLUME_ID, SNAPSHOT_ID
)
expected_refs = deepcopy(MANAGEABLE_PURE_SNAP_REFS)
expected_refs[0]['reference'] = {'name': purity_snaps[0]['name']}
expected_refs[0]['safe_to_manage'] = False
expected_refs[0]['reason_not_safe'] = 'Snapshot already managed.'
expected_refs[0]['cinder_id'] = SNAPSHOT_ID
self._test_get_manageable_things(
pure_objs=purity_snaps,
expected_refs=expected_refs,
cinder_objs=cinder_snaps,
pure_hosts=[PURE_HOST],
is_snapshot=True
)
def test_get_manageable_snapshots_no_pure_snapshots(self):
"""Expect no refs to be found if no snapshots are on Purity."""
self._test_get_manageable_things(pure_objs=[],
expected_refs=[],
pure_hosts=[PURE_HOST],
is_snapshot=True)
@mock.patch(BASE_DRIVER_OBJ + '._is_volume_replicated_type', autospec=True)
def test_retype_repl_to_repl(self, mock_is_replicated_type):
self._test_retype_repl(mock_is_replicated_type, True, True)

View File

@ -898,7 +898,7 @@ class PureBaseVolumeDriver(san.SanDriver):
"""
volume_info = self._validate_manage_existing_ref(existing_ref)
size = int(math.ceil(float(volume_info["size"]) / units.Gi))
size = self._round_bytes_to_gib(volume_info['size'])
return size
@ -974,7 +974,7 @@ class PureBaseVolumeDriver(san.SanDriver):
self._verify_manage_snap_api_requirements()
snap_info = self._validate_manage_existing_ref(existing_ref,
is_snap=True)
size = int(math.ceil(float(snap_info["size"]) / units.Gi))
size = self._round_bytes_to_gib(snap_info['size'])
return size
def unmanage_snapshot(self, snapshot):
@ -993,6 +993,100 @@ class PureBaseVolumeDriver(san.SanDriver):
"new_name": unmanaged_snap_name})
self._rename_volume_object(snap_name, unmanaged_snap_name)
def get_manageable_volumes(self, cinder_volumes, marker, limit, offset,
sort_keys, sort_dirs):
"""List volumes on the backend available for management by Cinder.
Rule out volumes that are attached to a Purity host or that
are already in the list of cinder_volumes. We return references
of the volume names for any others.
"""
array = self._get_current_array()
pure_vols = array.list_volumes()
hosts_with_connections = array.list_hosts(all=True)
# Put together a map of volumes that are connected to hosts
connected_vols = {}
for host in hosts_with_connections:
vol = host.get('vol')
if vol:
connected_vols[vol] = host['name']
# Put together a map of existing cinder volumes on the array
# so we can lookup cinder id's by purity volume names
existing_vols = {}
for cinder_vol in cinder_volumes:
existing_vols[self._get_vol_name(cinder_vol)] = cinder_vol.name_id
manageable_vols = []
for pure_vol in pure_vols:
vol_name = pure_vol['name']
cinder_id = existing_vols.get(vol_name)
is_safe = True
reason_not_safe = None
host = connected_vols.get(vol_name)
if host:
is_safe = False
reason_not_safe = _('Volume connected to host %s.') % host
if cinder_id:
is_safe = False
reason_not_safe = _('Volume already managed.')
manageable_vols.append({
'reference': {'name': vol_name},
'size': self._round_bytes_to_gib(pure_vol['size']),
'safe_to_manage': is_safe,
'reason_not_safe': reason_not_safe,
'cinder_id': cinder_id,
'extra_info': None,
})
return volume_utils.paginate_entries_list(
manageable_vols, marker, limit, offset, sort_keys, sort_dirs)
def get_manageable_snapshots(self, cinder_snapshots, marker, limit, offset,
sort_keys, sort_dirs):
"""List snapshots on the backend available for management by Cinder."""
array = self._get_current_array()
pure_snapshots = array.list_volumes(snap=True)
# Put together a map of existing cinder snapshots on the array
# so we can lookup cinder id's by purity snapshot names
existing_snapshots = {}
for cinder_snap in cinder_snapshots:
name = self._get_snap_name(cinder_snap)
existing_snapshots[name] = cinder_snap.id
manageable_snaps = []
for pure_snap in pure_snapshots:
snap_name = pure_snap['name']
cinder_id = existing_snapshots.get(snap_name)
is_safe = True
reason_not_safe = None
if cinder_id:
is_safe = False
reason_not_safe = _("Snapshot already managed.")
manageable_snaps.append({
'reference': {'name': snap_name},
'size': self._round_bytes_to_gib(pure_snap['size']),
'safe_to_manage': is_safe,
'reason_not_safe': reason_not_safe,
'cinder_id': cinder_id,
'extra_info': None,
'source_reference': {'name': pure_snap['source']},
})
return volume_utils.paginate_entries_list(
manageable_snaps, marker, limit, offset, sort_keys, sort_dirs)
@staticmethod
def _round_bytes_to_gib(size):
return int(math.ceil(float(size) / units.Gi))
def _get_flasharray(self, san_ip, api_token, rest_version=None,
verify_https=None, ssl_cert_path=None):

View File

@ -0,0 +1,4 @@
---
features:
- Add get_manageable_volumes and get_manageable_snapshots implementations for
Pure Storage Volume Drivers.