Merge "Add secondary account capability to SolidFire"
This commit is contained in:
@@ -180,6 +180,8 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'qos': None,
|
'qos': None,
|
||||||
'iqn': test_name}]}}
|
'iqn': test_name}]}}
|
||||||
return result
|
return result
|
||||||
|
elif method is 'DeleteSnapshot':
|
||||||
|
return {'result': {}}
|
||||||
else:
|
else:
|
||||||
# Crap, unimplemented API call in Fake
|
# Crap, unimplemented API call in Fake
|
||||||
return None
|
return None
|
||||||
@@ -206,13 +208,13 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
def fake_get_model_info(self, account, vid):
|
def fake_get_model_info(self, account, vid):
|
||||||
return {'fake': 'fake-model'}
|
return {'fake': 'fake-model'}
|
||||||
|
|
||||||
def test_create_with_qos_type(self):
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
'_issue_api_request',
|
def test_create_volume_with_qos_type(self,
|
||||||
self.fake_issue_api_request)
|
_mock_create_template_account,
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
_mock_issue_api_request):
|
||||||
'_set_qos_by_volume_type',
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
self.fake_set_qos_by_volume_type)
|
_mock_create_template_account.return_value = 1
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
@@ -220,52 +222,128 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'volume_type_id': 'fast',
|
'volume_type_id': 'fast',
|
||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
fake_sfaccounts = [{'accountID': 5,
|
||||||
model_update = sfv.create_volume(testvol)
|
'name': 'testprjid',
|
||||||
self.assertIsNotNone(model_update)
|
'targetSecret': 'shhhh',
|
||||||
|
'username': 'john-wayne'}]
|
||||||
|
|
||||||
def test_create_volume(self):
|
test_type = {'name': 'sf-1',
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
'qos_specs_id': 'fb0576d7-b4b5-4cad-85dc-ca92e6a497d1',
|
||||||
'_issue_api_request',
|
'deleted': False,
|
||||||
self.fake_issue_api_request)
|
'created_at': '2014-02-06 04:58:11',
|
||||||
|
'updated_at': None,
|
||||||
|
'extra_specs': {},
|
||||||
|
'deleted_at': None,
|
||||||
|
'id': 'e730e97b-bc7d-4af3-934a-32e59b218e81'}
|
||||||
|
|
||||||
|
test_qos_spec = {'id': 'asdfafdasdf',
|
||||||
|
'specs': {'minIOPS': '1000',
|
||||||
|
'maxIOPS': '2000',
|
||||||
|
'burstIOPS': '3000'}}
|
||||||
|
|
||||||
|
def _fake_get_volume_type(ctxt, type_id):
|
||||||
|
return test_type
|
||||||
|
|
||||||
|
def _fake_get_qos_spec(ctxt, spec_id):
|
||||||
|
return test_qos_spec
|
||||||
|
|
||||||
|
def _fake_do_volume_create(account, params):
|
||||||
|
return params
|
||||||
|
|
||||||
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
|
with mock.patch.object(sfv,
|
||||||
|
'_get_sfaccounts_for_tenant',
|
||||||
|
return_value=fake_sfaccounts), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_issue_api_request',
|
||||||
|
side_effect=self.fake_issue_api_request), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_account_create_availability',
|
||||||
|
return_value=fake_sfaccounts[0]), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_do_volume_create',
|
||||||
|
side_effect=_fake_do_volume_create), \
|
||||||
|
mock.patch.object(volume_types,
|
||||||
|
'get_volume_type',
|
||||||
|
side_effect=_fake_get_volume_type), \
|
||||||
|
mock.patch.object(qos_specs,
|
||||||
|
'get_qos_specs',
|
||||||
|
side_effect=_fake_get_qos_spec):
|
||||||
|
|
||||||
|
self.assertEqual({'burstIOPS': 3000,
|
||||||
|
'minIOPS': 1000,
|
||||||
|
'maxIOPS': 2000},
|
||||||
|
sfv.create_volume(testvol)['qos'])
|
||||||
|
|
||||||
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
|
def test_create_volume(self,
|
||||||
|
_mock_create_template_account,
|
||||||
|
_mock_issue_api_request):
|
||||||
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
|
_mock_create_template_account.return_value = 1
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||||
'volume_type_id': None,
|
'volume_type_id': None,
|
||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
fake_sfaccounts = [{'accountID': 5,
|
||||||
|
'name': 'testprjid',
|
||||||
|
'targetSecret': 'shhhh',
|
||||||
|
'username': 'john-wayne'}]
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
model_update = sfv.create_volume(testvol)
|
with mock.patch.object(sfv,
|
||||||
self.assertIsNotNone(model_update)
|
'_get_sfaccounts_for_tenant',
|
||||||
self.assertIsNone(model_update.get('provider_geometry', None))
|
return_value=fake_sfaccounts), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_issue_api_request',
|
||||||
|
side_effect=self.fake_issue_api_request), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_account_create_availability',
|
||||||
|
return_value=fake_sfaccounts[0]):
|
||||||
|
|
||||||
def test_create_volume_non_512(self):
|
model_update = sfv.create_volume(testvol)
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
self.assertIsNotNone(model_update)
|
||||||
'_issue_api_request',
|
self.assertIsNone(model_update.get('provider_geometry', None))
|
||||||
self.fake_issue_api_request)
|
|
||||||
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
|
def test_create_volume_non_512e(self,
|
||||||
|
_mock_create_template_account,
|
||||||
|
_mock_issue_api_request):
|
||||||
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
|
_mock_create_template_account.return_value = 1
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||||
'volume_type_id': None,
|
'volume_type_id': None,
|
||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
fake_sfaccounts = [{'accountID': 5,
|
||||||
|
'name': 'testprjid',
|
||||||
|
'targetSecret': 'shhhh',
|
||||||
|
'username': 'john-wayne'}]
|
||||||
|
|
||||||
self.configuration.sf_emulate_512 = False
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
model_update = sfv.create_volume(testvol)
|
with mock.patch.object(sfv,
|
||||||
self.assertEqual(model_update.get('provider_geometry', None),
|
'_get_sfaccounts_for_tenant',
|
||||||
'4096 4096')
|
return_value=fake_sfaccounts), \
|
||||||
self.configuration.sf_emulate_512 = True
|
mock.patch.object(sfv,
|
||||||
|
'_issue_api_request',
|
||||||
|
side_effect=self.fake_issue_api_request), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_account_create_availability',
|
||||||
|
return_value=fake_sfaccounts[0]):
|
||||||
|
|
||||||
|
self.configuration.sf_emulate_512 = False
|
||||||
|
model_update = sfv.create_volume(testvol)
|
||||||
|
self.configuration.sf_emulate_512 = True
|
||||||
|
self.assertEqual(model_update.get('provider_geometry', None),
|
||||||
|
'4096 4096')
|
||||||
|
|
||||||
def test_create_delete_snapshot(self):
|
def test_create_delete_snapshot(self):
|
||||||
testvol = {'project_id': 'testprjid',
|
|
||||||
'name': 'testvol',
|
|
||||||
'size': 1,
|
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
|
||||||
'volume_type_id': None,
|
|
||||||
'created_at': timeutils.utcnow()}
|
|
||||||
|
|
||||||
testsnap = {'project_id': 'testprjid',
|
testsnap = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'volume_size': 1,
|
'volume_size': 1,
|
||||||
@@ -275,21 +353,26 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
sfv.create_volume(testvol)
|
|
||||||
sfv.create_snapshot(testsnap)
|
sfv.create_snapshot(testsnap)
|
||||||
with mock.patch.object(solidfire.SolidFireDriver,
|
with mock.patch.object(solidfire.SolidFireDriver,
|
||||||
'_get_sf_snapshots',
|
'_get_sf_snapshots',
|
||||||
return_value=[{'snapshotID': '1',
|
return_value=[{'snapshotID': '1',
|
||||||
'name': 'testvol'}]):
|
'name': 'UUID-b831c4d1-d1f0-11e1-9b23-0800200c9a66'}]), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_sfaccounts_for_tenant',
|
||||||
|
return_value=[{'accountID': 5,
|
||||||
|
'name': 'testprjid'}]):
|
||||||
sfv.delete_snapshot(testsnap)
|
sfv.delete_snapshot(testsnap)
|
||||||
|
|
||||||
def test_create_clone(self):
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
'_issue_api_request',
|
def test_create_clone(self,
|
||||||
self.fake_issue_api_request)
|
_mock_create_template_account,
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
_mock_issue_api_request):
|
||||||
'_get_model_info',
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
self.fake_get_model_info)
|
_mock_create_template_account.return_value = 1
|
||||||
|
_fake_get_snaps = [{'snapshotID': 5, 'name': 'testvol'}]
|
||||||
|
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
@@ -304,10 +387,19 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'volume_type_id': None,
|
'volume_type_id': None,
|
||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
|
||||||
with mock.patch.object(solidfire.SolidFireDriver,
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
|
with mock.patch.object(sfv,
|
||||||
'_get_sf_snapshots',
|
'_get_sf_snapshots',
|
||||||
return_value=[]):
|
return_value=_fake_get_snaps), \
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
mock.patch.object(sfv,
|
||||||
|
'_issue_api_request',
|
||||||
|
side_effect=self.fake_issue_api_request), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_sfaccounts_for_tenant',
|
||||||
|
return_value=[]), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_model_info',
|
||||||
|
return_value={}):
|
||||||
sfv.create_cloned_volume(testvol_b, testvol)
|
sfv.create_cloned_volume(testvol_b, testvol)
|
||||||
|
|
||||||
def test_initialize_connector_with_blocksizes(self):
|
def test_initialize_connector_with_blocksizes(self):
|
||||||
@@ -331,25 +423,6 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
self.assertEqual('4096', properties['data']['physical_block_size'])
|
self.assertEqual('4096', properties['data']['physical_block_size'])
|
||||||
self.assertEqual('4096', properties['data']['logical_block_size'])
|
self.assertEqual('4096', properties['data']['logical_block_size'])
|
||||||
|
|
||||||
def test_create_volume_with_qos(self):
|
|
||||||
preset_qos = {}
|
|
||||||
preset_qos['qos'] = 'fast'
|
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
|
||||||
'_issue_api_request',
|
|
||||||
self.fake_issue_api_request)
|
|
||||||
|
|
||||||
testvol = {'project_id': 'testprjid',
|
|
||||||
'name': 'testvol',
|
|
||||||
'size': 1,
|
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
|
||||||
'metadata': [preset_qos],
|
|
||||||
'volume_type_id': None,
|
|
||||||
'created_at': timeutils.utcnow()}
|
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
|
||||||
model_update = sfv.create_volume(testvol)
|
|
||||||
self.assertIsNotNone(model_update)
|
|
||||||
|
|
||||||
def test_create_volume_fails(self):
|
def test_create_volume_fails(self):
|
||||||
# NOTE(JDG) This test just fakes update_cluster_status
|
# NOTE(JDG) This test just fakes update_cluster_status
|
||||||
# this is inentional for this test
|
# this is inentional for this test
|
||||||
@@ -403,18 +476,41 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
account = sfv._get_sfaccount_by_name('some-name')
|
account = sfv._get_sfaccount_by_name('some-name')
|
||||||
self.assertIsNone(account)
|
self.assertIsNone(account)
|
||||||
|
|
||||||
def test_delete_volume(self):
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
'_issue_api_request',
|
def test_delete_volume(self,
|
||||||
self.fake_issue_api_request)
|
_mock_create_template_account,
|
||||||
|
_mock_issue_api_request):
|
||||||
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
|
_mock_create_template_account.return_value = 1
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'test_volume',
|
'name': 'test_volume',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||||
'created_at': timeutils.utcnow()}
|
'created_at': timeutils.utcnow()}
|
||||||
|
fake_sfaccounts = [{'accountID': 5,
|
||||||
|
'name': 'testprjid',
|
||||||
|
'targetSecret': 'shhhh',
|
||||||
|
'username': 'john-wayne'}]
|
||||||
|
|
||||||
|
def _fake_do_v_create(project_id, params):
|
||||||
|
return project_id, params
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
sfv.delete_volume(testvol)
|
with mock.patch.object(sfv,
|
||||||
|
'_get_sfaccounts_for_tenant',
|
||||||
|
return_value=fake_sfaccounts), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_issue_api_request',
|
||||||
|
side_effect=self.fake_issue_api_request), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_get_account_create_availability',
|
||||||
|
return_value=fake_sfaccounts[0]), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_do_volume_create',
|
||||||
|
side_effect=_fake_do_v_create):
|
||||||
|
|
||||||
|
sfv.delete_volume(testvol)
|
||||||
|
|
||||||
def test_delete_volume_fails_no_volume(self):
|
def test_delete_volume_fails_no_volume(self):
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
self.stubs.Set(solidfire.SolidFireDriver,
|
||||||
@@ -433,26 +529,6 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_delete_volume_fails_account_lookup(self):
|
|
||||||
# NOTE(JDG) This test just fakes update_cluster_status
|
|
||||||
# this is inentional for this test
|
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
|
||||||
'_update_cluster_status',
|
|
||||||
self.fake_update_cluster_status)
|
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
|
||||||
'_issue_api_request',
|
|
||||||
self.fake_issue_api_request_fails)
|
|
||||||
testvol = {'project_id': 'testprjid',
|
|
||||||
'name': 'no-name',
|
|
||||||
'size': 1,
|
|
||||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
|
||||||
'created_at': timeutils.utcnow()}
|
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
|
||||||
self.assertRaises(exception.SolidFireAccountNotFound,
|
|
||||||
sfv.delete_volume,
|
|
||||||
testvol)
|
|
||||||
|
|
||||||
def test_get_cluster_info(self):
|
def test_get_cluster_info(self):
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
self.stubs.Set(solidfire.SolidFireDriver,
|
||||||
'_issue_api_request',
|
'_issue_api_request',
|
||||||
@@ -690,16 +766,13 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
self.assertIsNotNone(model_update)
|
self.assertIsNotNone(model_update)
|
||||||
self.assertIsNone(model_update.get('provider_geometry', None))
|
self.assertIsNone(model_update.get('provider_geometry', None))
|
||||||
|
|
||||||
def test_create_volume_for_migration(self):
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
def _fake_do_v_create(self, project_id, params):
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
return project_id, params
|
def test_create_volume_for_migration(self,
|
||||||
|
_mock_create_template_account,
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
_mock_issue_api_request):
|
||||||
'_issue_api_request',
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
self.fake_issue_api_request)
|
_mock_create_template_account.return_value = 1
|
||||||
self.stubs.Set(solidfire.SolidFireDriver,
|
|
||||||
'_do_volume_create', _fake_do_v_create)
|
|
||||||
|
|
||||||
testvol = {'project_id': 'testprjid',
|
testvol = {'project_id': 'testprjid',
|
||||||
'name': 'testvol',
|
'name': 'testvol',
|
||||||
'size': 1,
|
'size': 1,
|
||||||
@@ -708,15 +781,35 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'created_at': timeutils.utcnow(),
|
'created_at': timeutils.utcnow(),
|
||||||
'migration_status': 'target:'
|
'migration_status': 'target:'
|
||||||
'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
||||||
|
fake_sfaccounts = [{'accountID': 5,
|
||||||
|
'name': 'testprjid',
|
||||||
|
'targetSecret': 'shhhh',
|
||||||
|
'username': 'john-wayne'}]
|
||||||
|
|
||||||
|
def _fake_do_v_create(project_id, params):
|
||||||
|
return project_id, params
|
||||||
|
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
proj_id, sf_vol_object = sfv.create_volume(testvol)
|
with mock.patch.object(sfv,
|
||||||
self.assertEqual('a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
'_get_sfaccounts_for_tenant',
|
||||||
sf_vol_object['attributes']['uuid'])
|
return_value=fake_sfaccounts), \
|
||||||
self.assertEqual('b830b3c0-d1f0-11e1-9b23-1900200c9a77',
|
mock.patch.object(sfv,
|
||||||
sf_vol_object['attributes']['migration_uuid'])
|
'_issue_api_request',
|
||||||
self.assertEqual('UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
side_effect=self.fake_issue_api_request), \
|
||||||
sf_vol_object['name'])
|
mock.patch.object(sfv,
|
||||||
|
'_get_account_create_availability',
|
||||||
|
return_value=fake_sfaccounts[0]), \
|
||||||
|
mock.patch.object(sfv,
|
||||||
|
'_do_volume_create',
|
||||||
|
side_effect=_fake_do_v_create):
|
||||||
|
|
||||||
|
proj_id, sf_vol_object = sfv.create_volume(testvol)
|
||||||
|
self.assertEqual('a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||||
|
sf_vol_object['attributes']['uuid'])
|
||||||
|
self.assertEqual('b830b3c0-d1f0-11e1-9b23-1900200c9a77',
|
||||||
|
sf_vol_object['attributes']['migration_uuid'])
|
||||||
|
self.assertEqual('UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||||
|
sf_vol_object['name'])
|
||||||
|
|
||||||
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
|
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
|
||||||
@@ -808,9 +901,10 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
self.fake_image_meta,
|
self.fake_image_meta,
|
||||||
'fake'))
|
'fake'))
|
||||||
|
|
||||||
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
def test_clone_image_authorization(self, _mock_issue_api_request):
|
def test_clone_image_authorization(self, _mock_create_template_account):
|
||||||
_mock_issue_api_request.return_value = self.mock_stats_data
|
_mock_create_template_account.return_value = 1
|
||||||
|
|
||||||
self.configuration.sf_allow_template_caching = True
|
self.configuration.sf_allow_template_caching = True
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
|
|
||||||
@@ -824,33 +918,40 @@ class SolidFireVolumeTestCase(test.TestCase):
|
|||||||
'properties': {'virtual_size': 1},
|
'properties': {'virtual_size': 1},
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
'owner': 'wrong-owner'}
|
'owner': 'wrong-owner'}
|
||||||
self.assertEqual((None, False),
|
with mock.patch.object(sfv, '_do_clone_volume',
|
||||||
sfv.clone_image(self.ctxt,
|
return_value=('fe', 'fi', 'fo')):
|
||||||
self.mock_volume,
|
self.assertEqual((None, False),
|
||||||
'fake',
|
sfv.clone_image(self.ctxt,
|
||||||
_fake_image_meta,
|
self.mock_volume,
|
||||||
'fake'))
|
'fake',
|
||||||
|
_fake_image_meta,
|
||||||
|
'fake'))
|
||||||
|
|
||||||
# And is_public False, but the correct owner does work
|
# And is_public False, but the correct owner does work
|
||||||
# expect raise AccountNotFound as that's the next call after
|
_fake_image_meta['owner'] = 'testprjid'
|
||||||
# auth checks
|
self.assertEqual(('fo', True), sfv.clone_image(self.ctxt,
|
||||||
_fake_image_meta['owner'] = 'testprjid'
|
self.mock_volume,
|
||||||
self.assertRaises(exception.SolidFireAccountNotFound,
|
'fake',
|
||||||
sfv.clone_image, self.ctxt,
|
_fake_image_meta,
|
||||||
self.mock_volume, 'fake',
|
'fake'))
|
||||||
_fake_image_meta, 'fake')
|
|
||||||
|
|
||||||
# And is_public True, even if not the correct owner
|
# And is_public True, even if not the correct owner
|
||||||
_fake_image_meta['is_public'] = True
|
_fake_image_meta['is_public'] = True
|
||||||
_fake_image_meta['owner'] = 'wrong-owner'
|
_fake_image_meta['owner'] = 'wrong-owner'
|
||||||
self.assertRaises(exception.SolidFireAccountNotFound,
|
self.assertEqual(('fo', True), sfv.clone_image(self.ctxt,
|
||||||
sfv.clone_image, self.ctxt,
|
self.mock_volume,
|
||||||
self.mock_volume, 'fake',
|
'fake',
|
||||||
_fake_image_meta, 'fake')
|
_fake_image_meta,
|
||||||
|
'fake'))
|
||||||
|
|
||||||
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
|
||||||
def test_clone_image_virt_size_not_set(self, _mock_issue_api_request):
|
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||||
|
def test_clone_image_virt_size_not_set(self,
|
||||||
|
_mock_create_template_account,
|
||||||
|
_mock_issue_api_request):
|
||||||
_mock_issue_api_request.return_value = self.mock_stats_data
|
_mock_issue_api_request.return_value = self.mock_stats_data
|
||||||
|
_mock_create_template_account.return_value = 1
|
||||||
|
|
||||||
self.configuration.sf_allow_template_caching = True
|
self.configuration.sf_allow_template_caching = True
|
||||||
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
|
||||||
|
|
||||||
|
|||||||
@@ -112,10 +112,11 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
1.2.2 - Catch VolumeNotFound on accept xfr
|
1.2.2 - Catch VolumeNotFound on accept xfr
|
||||||
2.0.0 - Move from httplib to requests
|
2.0.0 - Move from httplib to requests
|
||||||
2.0.1 - Implement SolidFire Snapshots
|
2.0.1 - Implement SolidFire Snapshots
|
||||||
|
2.0.2 - Implement secondary account
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = '2.0.1'
|
VERSION = '2.0.2'
|
||||||
|
|
||||||
sf_qos_dict = {'slow': {'minIOPS': 100,
|
sf_qos_dict = {'slow': {'minIOPS': 100,
|
||||||
'maxIOPS': 200,
|
'maxIOPS': 200,
|
||||||
@@ -146,27 +147,29 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
super(SolidFireDriver, self).__init__(*args, **kwargs)
|
super(SolidFireDriver, self).__init__(*args, **kwargs)
|
||||||
self.configuration.append_config_values(sf_opts)
|
self.configuration.append_config_values(sf_opts)
|
||||||
self._endpoint = self._build_endpoint_info()
|
self._endpoint = self._build_endpoint_info()
|
||||||
|
self.template_account_id = None
|
||||||
|
self.max_volumes_per_account = 1990
|
||||||
try:
|
try:
|
||||||
self._update_cluster_status()
|
self._update_cluster_status()
|
||||||
except exception.SolidFireAPIException:
|
except exception.SolidFireAPIException:
|
||||||
pass
|
pass
|
||||||
if self.configuration.sf_allow_template_caching:
|
if self.configuration.sf_allow_template_caching:
|
||||||
account = self.configuration.sf_template_account_name
|
account = self.configuration.sf_template_account_name
|
||||||
self._create_template_account(account)
|
self.template_account_id = self._create_template_account(account)
|
||||||
|
|
||||||
def _create_template_account(self, account_name):
|
def _create_template_account(self, account_name):
|
||||||
chap_secret = self._generate_random_string(12)
|
id = self._issue_api_request(
|
||||||
params = {'username': account_name,
|
'GetAccountByName',
|
||||||
'initiatorSecret': chap_secret,
|
{'username': account_name})['result']['account']['accountID']
|
||||||
'targetSecret': chap_secret,
|
if not id:
|
||||||
'attributes': {}}
|
chap_secret = self._generate_random_string(12)
|
||||||
try:
|
params = {'username': account_name,
|
||||||
self._issue_api_request('AddAccount', params)
|
'initiatorSecret': chap_secret,
|
||||||
except exception.SolidFireAPIException as ex:
|
'targetSecret': chap_secret,
|
||||||
if 'DuplicateUsername' in ex.msg:
|
'attributes': {}}
|
||||||
pass
|
id = self._issue_api_request('AddAccount',
|
||||||
else:
|
params)['result']['accountID']
|
||||||
raise
|
return id
|
||||||
|
|
||||||
def _build_endpoint_info(self, **kwargs):
|
def _build_endpoint_info(self, **kwargs):
|
||||||
endpoint = {}
|
endpoint = {}
|
||||||
@@ -347,64 +350,59 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
|
|
||||||
def _do_clone_volume(self, src_uuid,
|
def _do_clone_volume(self, src_uuid,
|
||||||
src_project_id,
|
src_project_id,
|
||||||
v_ref):
|
vref):
|
||||||
"""Create a clone of an existing volume.
|
"""Create a clone of an existing volume or snapshot. """
|
||||||
|
|
||||||
Currently snapshots are the same as clones on the SF cluster.
|
|
||||||
Due to the way the SF cluster works there's no loss in efficiency
|
|
||||||
or space usage between the two. The only thing different right
|
|
||||||
now is the restore snapshot functionality which has not been
|
|
||||||
implemented in the pre-release version of the SolidFire Cluster.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
attributes = {}
|
attributes = {}
|
||||||
qos = {}
|
qos = {}
|
||||||
|
|
||||||
sfaccount = self._get_sfaccount(src_project_id)
|
sf_accounts = self._get_sfaccounts_for_tenant(vref['project_id'])
|
||||||
if src_project_id != v_ref['project_id']:
|
if not sf_accounts:
|
||||||
sfaccount = self._create_sfaccount(v_ref['project_id'])
|
sf_account = self._create_sfaccount(vref['project_id'])
|
||||||
|
|
||||||
if v_ref.get('size', None):
|
|
||||||
new_size = v_ref['size']
|
|
||||||
else:
|
else:
|
||||||
new_size = v_ref['volume_size']
|
# Check availability for creates
|
||||||
|
sf_account = self._get_account_create_availability(sf_accounts)
|
||||||
|
if not sf_account:
|
||||||
|
# TODO(jdg): We're not doing tertiaries, so fail
|
||||||
|
msg = _('volumes/account exceeded on both primary '
|
||||||
|
'and secondary SolidFire accounts')
|
||||||
|
raise exception.SolidFireDriverException(msg)
|
||||||
|
|
||||||
params = {'name': 'UUID-%s' % v_ref['id'],
|
params = {'name': 'UUID-%s' % vref['id'],
|
||||||
'newSize': int(new_size * units.Gi),
|
'newAccountID': sf_account['accountID']}
|
||||||
'newAccountID': sfaccount['accountID']}
|
|
||||||
|
|
||||||
# NOTE(jdg): First check the SF snapshots
|
# NOTE(jdg): First check the SF snapshots
|
||||||
# if we don't find a snap by the given name, just move on to check
|
# if we don't find a snap by the given name, just move on to check
|
||||||
# volumes. This may be a running system that was updated from
|
# volumes. This may be a running system that was updated from
|
||||||
# before we did snapshots, so need to check both
|
# before we did snapshots, so need to check both
|
||||||
|
is_clone = False
|
||||||
snap_name = 'UUID-%s' % src_uuid
|
snap_name = 'UUID-%s' % src_uuid
|
||||||
snaps = self._get_sf_snapshots()
|
snaps = self._get_sf_snapshots()
|
||||||
snap = next((s for s in snaps if s["name"] == snap_name), None)
|
snap = next((s for s in snaps if s["name"] == snap_name), None)
|
||||||
is_clone = False
|
|
||||||
if snap:
|
if snap:
|
||||||
params['snapshotID'] = int(snap['snapshotID'])
|
params['snapshotID'] = int(snap['snapshotID'])
|
||||||
params['volumeID'] = int(snap['volumeID'])
|
params['volumeID'] = int(snap['volumeID'])
|
||||||
|
params['newSize'] = int(vref['size'] * units.Gi)
|
||||||
else:
|
else:
|
||||||
sf_vol = self._get_sf_volume(
|
sf_vol = self._get_sf_volume(
|
||||||
src_uuid, {'accountID': sfaccount['accountID']})
|
src_uuid, {'accountID': sf_account['accountID']})
|
||||||
if sf_vol is None:
|
if sf_vol is None:
|
||||||
raise exception.VolumeNotFound(volume_id=src_uuid)
|
raise exception.VolumeNotFound(volume_id=src_uuid)
|
||||||
params['volumeID'] = int(sf_vol['volumeID'])
|
params['volumeID'] = int(sf_vol['volumeID'])
|
||||||
|
params['newSize'] = int(vref['size'] * units.Gi)
|
||||||
is_clone = True
|
is_clone = True
|
||||||
|
|
||||||
data = self._issue_api_request('CloneVolume', params, version='6.0')
|
data = self._issue_api_request('CloneVolume', params, version='6.0')
|
||||||
if (('result' not in data) or ('volumeID' not in data['result'])):
|
if (('result' not in data) or ('volumeID' not in data['result'])):
|
||||||
msg = _("API response: %s") % data
|
msg = _("API response: %s") % data
|
||||||
raise exception.SolidFireAPIException(msg)
|
raise exception.SolidFireAPIException(msg)
|
||||||
sf_volume_id = data['result']['volumeID']
|
|
||||||
|
|
||||||
|
sf_volume_id = data['result']['volumeID']
|
||||||
if (self.configuration.sf_allow_tenant_qos and
|
if (self.configuration.sf_allow_tenant_qos and
|
||||||
v_ref.get('volume_metadata')is not None):
|
vref.get('volume_metadata')is not None):
|
||||||
qos = self._set_qos_presets(v_ref)
|
qos = self._set_qos_presets(vref)
|
||||||
|
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
type_id = v_ref.get('volume_type_id', None)
|
type_id = vref.get('volume_type_id', None)
|
||||||
if type_id is not None:
|
if type_id is not None:
|
||||||
qos = self._set_qos_by_volume_type(ctxt, type_id)
|
qos = self._set_qos_by_volume_type(ctxt, type_id)
|
||||||
|
|
||||||
@@ -412,8 +410,8 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
# to set any that were provided
|
# to set any that were provided
|
||||||
params = {'volumeID': sf_volume_id}
|
params = {'volumeID': sf_volume_id}
|
||||||
|
|
||||||
create_time = v_ref['created_at'].isoformat()
|
create_time = vref['created_at'].isoformat()
|
||||||
attributes = {'uuid': v_ref['id'],
|
attributes = {'uuid': vref['id'],
|
||||||
'is_clone': 'True',
|
'is_clone': 'True',
|
||||||
'src_uuid': src_uuid,
|
'src_uuid': src_uuid,
|
||||||
'created_at': create_time}
|
'created_at': create_time}
|
||||||
@@ -425,7 +423,7 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
params['attributes'] = attributes
|
params['attributes'] = attributes
|
||||||
data = self._issue_api_request('ModifyVolume', params)
|
data = self._issue_api_request('ModifyVolume', params)
|
||||||
|
|
||||||
model_update = self._get_model_info(sfaccount, sf_volume_id)
|
model_update = self._get_model_info(sf_account, sf_volume_id)
|
||||||
if model_update is None:
|
if model_update is None:
|
||||||
mesg = _('Failed to get model update from clone')
|
mesg = _('Failed to get model update from clone')
|
||||||
raise exception.SolidFireAPIException(mesg)
|
raise exception.SolidFireAPIException(mesg)
|
||||||
@@ -433,27 +431,27 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
# Increment the usage count, just for data collection
|
# Increment the usage count, just for data collection
|
||||||
# We're only doing this for clones, not create_from snaps
|
# We're only doing this for clones, not create_from snaps
|
||||||
if is_clone:
|
if is_clone:
|
||||||
cloned_count = sf_vol['attributes'].get('cloned_count', 0)
|
data = self._update_attributes(sf_vol)
|
||||||
cloned_count += 1
|
return (data, sf_account, model_update)
|
||||||
attributes = sf_vol['attributes']
|
|
||||||
attributes['cloned_count'] = cloned_count
|
|
||||||
|
|
||||||
params = {'volumeID': int(sf_vol['volumeID'])}
|
def _update_attributes(self, sf_vol):
|
||||||
params['attributes'] = attributes
|
cloned_count = sf_vol['attributes'].get('cloned_count', 0)
|
||||||
data = self._issue_api_request('ModifyVolume', params)
|
cloned_count += 1
|
||||||
return (data, sfaccount, model_update)
|
attributes = sf_vol['attributes']
|
||||||
|
attributes['cloned_count'] = cloned_count
|
||||||
|
|
||||||
def _do_volume_create(self, project_id, params):
|
params = {'volumeID': int(sf_vol['volumeID'])}
|
||||||
sfaccount = self._create_sfaccount(project_id)
|
params['attributes'] = attributes
|
||||||
params['accountID'] = sfaccount['accountID']
|
return self._issue_api_request('ModifyVolume', params)
|
||||||
|
|
||||||
|
def _do_volume_create(self, sf_account, params):
|
||||||
data = self._issue_api_request('CreateVolume', params)
|
data = self._issue_api_request('CreateVolume', params)
|
||||||
|
|
||||||
if (('result' not in data) or ('volumeID' not in data['result'])):
|
if (('result' not in data) or ('volumeID' not in data['result'])):
|
||||||
msg = _("Failed volume create: %s") % data
|
msg = _("Failed volume create: %s") % data
|
||||||
raise exception.SolidFireAPIException(msg)
|
raise exception.SolidFireAPIException(msg)
|
||||||
|
|
||||||
sf_volume_id = data['result']['volumeID']
|
sf_volume_id = data['result']['volumeID']
|
||||||
return self._get_model_info(sfaccount, sf_volume_id)
|
return self._get_model_info(sf_account, sf_volume_id)
|
||||||
|
|
||||||
def _do_snapshot_create(self, params):
|
def _do_snapshot_create(self, params):
|
||||||
data = self._issue_api_request('CreateSnapshot', params, version='6.0')
|
data = self._issue_api_request('CreateSnapshot', params, version='6.0')
|
||||||
@@ -569,17 +567,19 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
attributes['image_info']['image_created_at'] =\
|
attributes['image_info']['image_created_at'] =\
|
||||||
image_meta['created_at'].isoformat()
|
image_meta['created_at'].isoformat()
|
||||||
attributes['image_info']['image_id'] = image_meta['id']
|
attributes['image_info']['image_id'] = image_meta['id']
|
||||||
|
|
||||||
params = {'name': 'OpenStackIMG-%s' % image_id,
|
params = {'name': 'OpenStackIMG-%s' % image_id,
|
||||||
'accountID': None,
|
'accountID': self.template_account_id,
|
||||||
'sliceCount': 1,
|
'sliceCount': 1,
|
||||||
'totalSize': int(min_sz_in_bytes),
|
'totalSize': int(min_sz_in_bytes),
|
||||||
'enable512e': self.configuration.sf_emulate_512,
|
'enable512e': self.configuration.sf_emulate_512,
|
||||||
'attributes': attributes,
|
'attributes': attributes,
|
||||||
'qos': {}}
|
'qos': {}}
|
||||||
|
|
||||||
account = self.configuration.sf_template_account_name
|
sf_account = self._issue_api_request(
|
||||||
template_vol = self._do_volume_create(account, params)
|
'GetAccountByID',
|
||||||
|
{'accountID': self.template_account_id})
|
||||||
|
|
||||||
|
template_vol = self._do_volume_create(sf_account, params)
|
||||||
tvol = {}
|
tvol = {}
|
||||||
tvol['id'] = image_id
|
tvol['id'] = image_id
|
||||||
tvol['provider_location'] = template_vol['provider_location']
|
tvol['provider_location'] = template_vol['provider_location']
|
||||||
@@ -588,9 +588,6 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
connector = 'na'
|
connector = 'na'
|
||||||
conn = self.initialize_connection(tvol, connector)
|
conn = self.initialize_connection(tvol, connector)
|
||||||
attach_info = super(SolidFireDriver, self)._connect_device(conn)
|
attach_info = super(SolidFireDriver, self)._connect_device(conn)
|
||||||
|
|
||||||
sfaccount = self._get_sfaccount(account)
|
|
||||||
params = {'accountID': sfaccount['accountID']}
|
|
||||||
properties = 'na'
|
properties = 'na'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -625,10 +622,7 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
# If it's out of date, just delete it and we'll create a new one
|
# If it's out of date, just delete it and we'll create a new one
|
||||||
# Any other case we don't care and just return without doing anything
|
# Any other case we don't care and just return without doing anything
|
||||||
|
|
||||||
sfaccount = self._get_sfaccount(
|
params = {'accountID': self.template_account_id}
|
||||||
self.configuration.sf_template_account_name)
|
|
||||||
|
|
||||||
params = {'accountID': sfaccount['accountID']}
|
|
||||||
sf_vol = self._get_sf_volume(image_meta['id'], params)
|
sf_vol = self._get_sf_volume(image_meta['id'], params)
|
||||||
if sf_vol is None:
|
if sf_vol is None:
|
||||||
return
|
return
|
||||||
@@ -639,7 +633,7 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Bummer, it's been updated, delete it
|
# Bummer, it's been updated, delete it
|
||||||
params = {'accountID': sfaccount['accountID']}
|
params = {'accountID': self.template_account_id}
|
||||||
params['volumeID'] = sf_vol['volumeID']
|
params['volumeID'] = sf_vol['volumeID']
|
||||||
data = self._issue_api_request('DeleteVolume', params)
|
data = self._issue_api_request('DeleteVolume', params)
|
||||||
if 'result' not in data:
|
if 'result' not in data:
|
||||||
@@ -653,6 +647,75 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
msg = _("Failed to create SolidFire Image-Volume")
|
msg = _("Failed to create SolidFire Image-Volume")
|
||||||
raise exception.SolidFireAPIException(msg)
|
raise exception.SolidFireAPIException(msg)
|
||||||
|
|
||||||
|
def _get_sfaccounts_for_tenant(self, cinder_project_id):
|
||||||
|
data = self._issue_api_request('ListAccounts', {})
|
||||||
|
if 'result' not in data:
|
||||||
|
msg = _("API response: %s") % data
|
||||||
|
raise exception.SolidFireAPIException(msg)
|
||||||
|
|
||||||
|
# Note(jdg): On SF we map account-name to OpenStack's tenant ID
|
||||||
|
# we use tenantID in here to get secondaries that might exist
|
||||||
|
# Also: we expect this to be sorted, so we get the primary first
|
||||||
|
# in the list
|
||||||
|
return sorted([acc for acc in data['result']['accounts'] if
|
||||||
|
cinder_project_id in acc['username']])
|
||||||
|
|
||||||
|
def _get_all_active_volumes(self, cinder_uuid=None):
|
||||||
|
params = {}
|
||||||
|
data = self._issue_api_request('ListActiveVolumes',
|
||||||
|
params)
|
||||||
|
if 'result' not in data:
|
||||||
|
msg = _("Failed get active SolidFire volumes: %s") % data
|
||||||
|
raise exception.SolidFireAPIException(msg)
|
||||||
|
if cinder_uuid:
|
||||||
|
deleted_vols = ([v for v in data['result']['volumes'] if
|
||||||
|
cinder_uuid in v.name])
|
||||||
|
else:
|
||||||
|
deleted_vols = [v for v in data['result']['volumes']]
|
||||||
|
return deleted_vols
|
||||||
|
|
||||||
|
def _get_all_deleted_volumes(self, cinder_uuid=None):
|
||||||
|
params = {}
|
||||||
|
data = self._issue_api_request('ListDeletedVolumes',
|
||||||
|
params)
|
||||||
|
if 'result' not in data:
|
||||||
|
msg = _("Failed get Deleted SolidFire volumes: %s") % data
|
||||||
|
raise exception.SolidFireAPIException(msg)
|
||||||
|
if cinder_uuid:
|
||||||
|
deleted_vols = ([v for v in data['result']['volumes'] if
|
||||||
|
cinder_uuid in v['name']])
|
||||||
|
else:
|
||||||
|
deleted_vols = [v for v in data['result']['volumes']]
|
||||||
|
return deleted_vols
|
||||||
|
|
||||||
|
def _get_account_create_availability(self, accounts):
|
||||||
|
# we'll check both the primary and the secondary
|
||||||
|
# if it exists and return whichever one has count
|
||||||
|
# available.
|
||||||
|
for acc in accounts:
|
||||||
|
if self._get_volumes_for_account(
|
||||||
|
acc['accountID']) > self.max_volumes_per_account:
|
||||||
|
return acc
|
||||||
|
if len(accounts) == 1:
|
||||||
|
sfaccount = self._create_sfaccount(accounts[0]['name'] + '_')
|
||||||
|
return sfaccount
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_volumes_for_account(self, sf_account_id, cinder_uuid=None):
|
||||||
|
# ListVolumesForAccount gives both Active and Deleted
|
||||||
|
# we require the solidfire accountID, uuid of volume
|
||||||
|
# is optional
|
||||||
|
params = {'accountID': sf_account_id}
|
||||||
|
response = self._issue_api_request('ListVolumesForAccount',
|
||||||
|
params)
|
||||||
|
if cinder_uuid:
|
||||||
|
vlist = [v for v in response['result']['volumes'] if
|
||||||
|
cinder_uuid in v['name']]
|
||||||
|
else:
|
||||||
|
vlist = [v for v in response['result']['volumes']]
|
||||||
|
vlist = sorted(vlist, key=lambda k: k['volumeID'])
|
||||||
|
return vlist
|
||||||
|
|
||||||
def clone_image(self, context,
|
def clone_image(self, context,
|
||||||
volume, image_location,
|
volume, image_location,
|
||||||
image_meta, image_service):
|
image_meta, image_service):
|
||||||
@@ -739,8 +802,14 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
for k, v in qos.items():
|
for k, v in qos.items():
|
||||||
attributes[k] = str(v)
|
attributes[k] = str(v)
|
||||||
|
|
||||||
|
sf_accounts = self._get_sfaccounts_for_tenant(volume['project_id'])
|
||||||
|
if not sf_accounts:
|
||||||
|
sf_account = self._create_sfaccount(volume['project_id'])
|
||||||
|
else:
|
||||||
|
sf_account = self._get_account_create_availability(sf_accounts)
|
||||||
|
|
||||||
params = {'name': 'UUID-%s' % volume['id'],
|
params = {'name': 'UUID-%s' % volume['id'],
|
||||||
'accountID': None,
|
'accountID': sf_account['accountID'],
|
||||||
'sliceCount': slice_count,
|
'sliceCount': slice_count,
|
||||||
'totalSize': int(volume['size'] * units.Gi),
|
'totalSize': int(volume['size'] * units.Gi),
|
||||||
'enable512e': self.configuration.sf_emulate_512,
|
'enable512e': self.configuration.sf_emulate_512,
|
||||||
@@ -755,8 +824,7 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
params['name'] = 'UUID-%s' % v
|
params['name'] = 'UUID-%s' % v
|
||||||
params['attributes']['migration_uuid'] = volume['id']
|
params['attributes']['migration_uuid'] = volume['id']
|
||||||
params['attributes']['uuid'] = v
|
params['attributes']['uuid'] = v
|
||||||
|
return self._do_volume_create(sf_account, params)
|
||||||
return self._do_volume_create(volume['project_id'], params)
|
|
||||||
|
|
||||||
def create_cloned_volume(self, volume, src_vref):
|
def create_cloned_volume(self, volume, src_vref):
|
||||||
"""Create a clone of an existing volume."""
|
"""Create a clone of an existing volume."""
|
||||||
@@ -774,8 +842,8 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
volumeID is what's guaranteed unique.
|
volumeID is what's guaranteed unique.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
sfaccount = self._get_sfaccount(volume['project_id'])
|
accounts = self._get_sfaccounts_for_tenant(volume['project_id'])
|
||||||
if sfaccount is None:
|
if accounts is None:
|
||||||
LOG.error(_LE("Account for Volume ID %s was not found on "
|
LOG.error(_LE("Account for Volume ID %s was not found on "
|
||||||
"the SolidFire Cluster while attempting "
|
"the SolidFire Cluster while attempting "
|
||||||
"delete_volume operation!"), volume['id'])
|
"delete_volume operation!"), volume['id'])
|
||||||
@@ -783,8 +851,11 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
"successfully created."))
|
"successfully created."))
|
||||||
return
|
return
|
||||||
|
|
||||||
params = {'accountID': sfaccount['accountID']}
|
for acc in accounts:
|
||||||
sf_vol = self._get_sf_volume(volume['id'], params)
|
sf_vol = self._get_volumes_for_account(acc['accountID'],
|
||||||
|
volume['id'])[0]
|
||||||
|
if sf_vol:
|
||||||
|
break
|
||||||
|
|
||||||
if sf_vol is not None:
|
if sf_vol is not None:
|
||||||
params = {'volumeID': sf_vol['volumeID']}
|
params = {'volumeID': sf_vol['volumeID']}
|
||||||
@@ -812,28 +883,27 @@ class SolidFireDriver(san.SanISCSIDriver):
|
|||||||
def delete_snapshot(self, snapshot):
|
def delete_snapshot(self, snapshot):
|
||||||
"""Delete the specified snapshot from the SolidFire cluster."""
|
"""Delete the specified snapshot from the SolidFire cluster."""
|
||||||
sf_snap_name = 'UUID-%s' % snapshot['id']
|
sf_snap_name = 'UUID-%s' % snapshot['id']
|
||||||
sfaccount = self._get_sfaccount(snapshot['project_id'])
|
accounts = self._get_sfaccounts_for_tenant(snapshot['project_id'])
|
||||||
params = {'accountID': sfaccount['accountID'],
|
snap = None
|
||||||
'name': sf_snap_name}
|
for a in accounts:
|
||||||
params = {'accountID': sfaccount['accountID']}
|
params = {'accountID': a['accountID']}
|
||||||
|
sf_vol = self._get_sf_volume(snapshot['volume_id'], params)
|
||||||
# Get the parent volume of the snapshot
|
sf_snaps = self._get_sf_snapshots(sf_vol['volumeID'])
|
||||||
sf_vol = self._get_sf_volume(snapshot['volume_id'], params)
|
snap = next((s for s in sf_snaps if s["name"] == sf_snap_name),
|
||||||
sf_snaps = self._get_sf_snapshots(sf_vol['volumeID'])
|
None)
|
||||||
snap = next((s for s in sf_snaps if s["name"] == sf_snap_name), None)
|
if snap:
|
||||||
if snap:
|
params = {'snapshotID': snap['snapshotID']}
|
||||||
params = {'snapshotID': snap['snapshotID']}
|
data = self._issue_api_request('DeleteSnapshot',
|
||||||
data = self._issue_api_request('DeleteSnapshot',
|
params,
|
||||||
params,
|
version='6.0')
|
||||||
version='6.0')
|
if 'result' not in data:
|
||||||
if 'result' not in data:
|
msg = (_("Failed to delete SolidFire Snapshot: %s") %
|
||||||
msg = (_("Failed to delete SolidFire Snapshot: %s") %
|
data)
|
||||||
data)
|
raise exception.SolidFireAPIException(msg)
|
||||||
raise exception.SolidFireAPIException(msg)
|
return
|
||||||
else:
|
# Make sure it's not "old style" using clones as snaps
|
||||||
# Make sure it's not "old style" using clones as snaps
|
LOG.debug("Snapshot not found, checking old style clones.")
|
||||||
LOG.debug("Snapshot not found, checking old style clones.")
|
self.delete_volume(snapshot)
|
||||||
self.delete_volume(snapshot)
|
|
||||||
|
|
||||||
def create_snapshot(self, snapshot):
|
def create_snapshot(self, snapshot):
|
||||||
sfaccount = self._get_sfaccount(snapshot['project_id'])
|
sfaccount = self._get_sfaccount(snapshot['project_id'])
|
||||||
|
|||||||
Reference in New Issue
Block a user