Skip to content

Commit

Permalink
Implementation of new Nova Volume driver for SolidFire ISCSI SAN
Browse files Browse the repository at this point in the history
* Adds new SolidFire driver that subclasses nova.volume.san.SanISCSIDriver
* Adds unit tests for new driver
* Adds new exception subclasses in nova.exception
* Adds John Griffith to Authors
Implements solidfire-san-iscsidriver
Change-Id: I4dc7508ba08f5333cde74d4cfeaae3939c5d2b02
  • Loading branch information
j-griffith committed Jan 30, 2012
1 parent c104437 commit c9ac6e1
Show file tree
Hide file tree
Showing 4 changed files with 456 additions and 3 deletions.
1 change: 1 addition & 0 deletions Authors
Expand Up @@ -76,6 +76,7 @@ Joel Moore <joelbm24@gmail.com>
Johannes Erdfelt <johannes.erdfelt@rackspace.com>
John Dewey <john@dewey.ws>
John Garbutt <john.garbutt@citrix.com>
John Griffith <john.griffith@solidfire.com>
John Tran <jtran@attinteractive.com>
Jonathan Bryce <jbryce@jbryce.com>
Jordan Rinke <jordan@openstack.org>
Expand Down
12 changes: 12 additions & 0 deletions nova/exception.py
Expand Up @@ -219,6 +219,10 @@ class InvalidKeypair(Invalid):
message = _("Keypair data is invalid")


class SfJsonEncodeFailure(NovaException):
message = _("Failed to load data into json format")


class InvalidRequest(Invalid):
message = _("The request is invalid.")

Expand Down Expand Up @@ -398,6 +402,10 @@ class VolumeNotFound(NotFound):
message = _("Volume %(volume_id)s could not be found.")


class SfAccountNotFound(NotFound):
message = _("Unable to locate account %(account_name) on Solidfire device")


class VolumeNotFoundForInstance(VolumeNotFound):
message = _("Volume not found for instance %(instance_id)s.")

Expand Down Expand Up @@ -934,3 +942,7 @@ class AggregateHostConflict(Duplicate):

class AggregateHostExists(Duplicate):
message = _("Aggregate %(aggregate_id)s already has host %(host)s.")


class DuplicateSfVolumeNames(Duplicate):
message = _("Detected more than one volume with name %(vol_name)")
180 changes: 180 additions & 0 deletions nova/tests/test_SolidFireSanISCSIDriver.py
@@ -0,0 +1,180 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from nova import exception
from nova import log as logging
from nova.volume.san import SolidFireSanISCSIDriver as SFID
from nova import test

LOG = logging.getLogger('nova.tests.test_solidfire')


class SolidFireVolumeTestCase(test.TestCase):
def setUp(self):
super(SolidFireVolumeTestCase, self).setUp()
self.executes = []
self.account_not_found = False

def tearDown(self):
pass

def fake_issue_api_request(obj, method, params):
if method is 'GetClusterInfo':
LOG.info('Called Fake GetClusterInfo...')
results = {'result': {'clusterInfo':
{'name': 'fake-cluster',
'mvip': '1.1.1.1',
'svip': '1.1.1.1',
'uniqueID': 'unqid',
'repCount': 2,
'attributes': {}}}}
return results

elif method is 'AddAccount':
LOG.info('Called Fake AddAccount...')
return {'result': {'accountID': 25}, 'id': 1}

elif method is 'GetAccountByName':
LOG.info('Called Fake GetAccountByName...')
results = {'result': {'account': {
'accountID': 25,
'username': params['username'],
'status': 'active',
'initiatorSecret': '123456789012',
'targetSecret': '123456789012',
'attributes': {},
'volumes': [6, 7, 20]}},
"id": 1}
return results

elif method is 'CreateVolume':
LOG.info('Called Fake CreateVolume...')
return {'result': {'volumeID': 5}, 'id': 1}

elif method is 'DeleteVolume':
LOG.info('Called Fake DeleteVolume...')
return {'result': {}, 'id': 1}

elif method is 'ListVolumesForAccount':
LOG.info('Called Fake ListVolumesForAccount...')
result = {'result': {'volumes': [{
'volumeID': '5',
'name': 'test_volume',
'accountID': 25,
'sliceCount': 1,
'totalSize': 1048576 * 1024,
'enable512e': False,
'access': "readWrite",
'status': "active",
'attributes':None,
'qos':None}]}}
return result

else:
LOG.error('Crap, unimplemented API call in Fake:%s' % method)

def fake_issue_api_request_fails(obj, method, params):
return {'error': {
'code': 000,
'name': 'DummyError',
'message': 'This is a fake error response'},
'id': 1}

def test_create_volume(self):
SFID._issue_api_request = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1}
sfv = SFID()
model_update = sfv.create_volume(testvol)

def test_create_volume_fails(self):
SFID._issue_api_request = self.fake_issue_api_request_fails
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1}
sfv = SFID()
try:
sfv.create_volume(testvol)
self.fail("Should have thrown Error")
except:
pass

def test_create_sfaccount(self):
sfv = SFID()
SFID._issue_api_request = self.fake_issue_api_request
account = sfv._create_sfaccount('project-id')
self.assertNotEqual(account, None)

def test_create_sfaccount_fails(self):
sfv = SFID()
SFID._issue_api_request = self.fake_issue_api_request_fails
account = sfv._create_sfaccount('project-id')
self.assertEqual(account, None)

def test_get_sfaccount_by_name(self):
sfv = SFID()
SFID._issue_api_request = self.fake_issue_api_request
account = sfv._get_sfaccount_by_name('some-name')
self.assertNotEqual(account, None)

def test_get_sfaccount_by_name_fails(self):
sfv = SFID()
SFID._issue_api_request = self.fake_issue_api_request_fails
account = sfv._get_sfaccount_by_name('some-name')
self.assertEqual(account, None)

def test_delete_volume(self):
SFID._issue_api_request = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'test_volume',
'size': 1}
sfv = SFID()
model_update = sfv.delete_volume(testvol)

def test_delete_volume_fails_no_volume(self):
SFID._issue_api_request = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'no-name',
'size': 1}
sfv = SFID()
try:
model_update = sfv.delete_volume(testvol)
self.fail("Should have thrown Error")
except:
pass

def test_delete_volume_fails_account_lookup(self):
SFID._issue_api_request = self.fake_issue_api_request
testvol = {'project_id': 'testprjid',
'name': 'no-name',
'size': 1}
sfv = SFID()
self.assertRaises(exception.DuplicateSfVolumeNames,
sfv.delete_volume,
testvol)

def test_get_cluster_info(self):
SFID._issue_api_request = self.fake_issue_api_request
sfv = SFID()
sfv._get_cluster_info()

def test_get_cluster_info_fail(self):
SFID._issue_api_request = self.fake_issue_api_request_fails
sfv = SFID()
self.assertRaises(exception.ApiError,
sfv._get_cluster_info)

0 comments on commit c9ac6e1

Please sign in to comment.