Removed volume specific tests
This commit is contained in:
parent
a005c85ef1
commit
35fe8d3b7f
|
@ -1,860 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Tests for Backup code.
|
||||
"""
|
||||
|
||||
import json
|
||||
from xml.dom import minidom
|
||||
|
||||
import webob
|
||||
|
||||
# needed for stubs to work
|
||||
import manila.backup
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
# needed for stubs to work
|
||||
import manila.volume
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BackupsAPITestCase(test.TestCase):
|
||||
"""Test Case for backups API."""
|
||||
|
||||
def setUp(self):
|
||||
super(BackupsAPITestCase, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
super(BackupsAPITestCase, self).tearDown()
|
||||
|
||||
@staticmethod
|
||||
def _create_backup(volume_id=1,
|
||||
display_name='test_backup',
|
||||
display_description='this is a test backup',
|
||||
container='volumebackups',
|
||||
status='creating',
|
||||
size=0, object_count=0):
|
||||
"""Create a backup object."""
|
||||
backup = {}
|
||||
backup['volume_id'] = volume_id
|
||||
backup['user_id'] = 'fake'
|
||||
backup['project_id'] = 'fake'
|
||||
backup['host'] = 'testhost'
|
||||
backup['availability_zone'] = 'az1'
|
||||
backup['display_name'] = display_name
|
||||
backup['display_description'] = display_description
|
||||
backup['container'] = container
|
||||
backup['status'] = status
|
||||
backup['fail_reason'] = ''
|
||||
backup['size'] = size
|
||||
backup['object_count'] = object_count
|
||||
return db.backup_create(context.get_admin_context(), backup)['id']
|
||||
|
||||
@staticmethod
|
||||
def _get_backup_attrib(backup_id, attrib_name):
|
||||
return db.backup_get(context.get_admin_context(),
|
||||
backup_id)[attrib_name]
|
||||
|
||||
@staticmethod
|
||||
def _create_volume(display_name='test_volume',
|
||||
display_description='this is a test volume',
|
||||
status='creating',
|
||||
size=1):
|
||||
"""Create a volume object."""
|
||||
vol = {}
|
||||
vol['size'] = size
|
||||
vol['user_id'] = 'fake'
|
||||
vol['project_id'] = 'fake'
|
||||
vol['status'] = status
|
||||
vol['display_name'] = display_name
|
||||
vol['display_description'] = display_description
|
||||
vol['attach_status'] = 'detached'
|
||||
return db.volume_create(context.get_admin_context(), vol)['id']
|
||||
|
||||
def test_show_backup(self):
|
||||
volume_id = self._create_volume(size=5)
|
||||
backup_id = self._create_backup(volume_id)
|
||||
LOG.debug('Created backup with id %s' % backup_id)
|
||||
req = webob.Request.blank('/v2/fake/backups/%s' %
|
||||
backup_id)
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res_dict['backup']['availability_zone'], 'az1')
|
||||
self.assertEqual(res_dict['backup']['container'], 'volumebackups')
|
||||
self.assertEqual(res_dict['backup']['description'],
|
||||
'this is a test backup')
|
||||
self.assertEqual(res_dict['backup']['name'], 'test_backup')
|
||||
self.assertEqual(res_dict['backup']['id'], backup_id)
|
||||
self.assertEqual(res_dict['backup']['object_count'], 0)
|
||||
self.assertEqual(res_dict['backup']['size'], 0)
|
||||
self.assertEqual(res_dict['backup']['status'], 'creating')
|
||||
self.assertEqual(res_dict['backup']['volume_id'], volume_id)
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_show_backup_xml_content_type(self):
|
||||
volume_id = self._create_volume(size=5)
|
||||
backup_id = self._create_backup(volume_id)
|
||||
req = webob.Request.blank('/v2/fake/backups/%s' % backup_id)
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/xml'
|
||||
req.headers['Accept'] = 'application/xml'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
dom = minidom.parseString(res.body)
|
||||
backup = dom.getElementsByTagName('backup')
|
||||
name = backup.item(0).getAttribute('name')
|
||||
container_name = backup.item(0).getAttribute('container')
|
||||
self.assertEquals(container_name.strip(), "volumebackups")
|
||||
self.assertEquals(name.strip(), "test_backup")
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_show_backup_with_backup_NotFound(self):
|
||||
req = webob.Request.blank('/v2/fake/backups/9999')
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['code'], 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['message'],
|
||||
'Backup 9999 could not be found.')
|
||||
|
||||
def test_list_backups_json(self):
|
||||
backup_id1 = self._create_backup()
|
||||
backup_id2 = self._create_backup()
|
||||
backup_id3 = self._create_backup()
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(len(res_dict['backups'][0]), 3)
|
||||
self.assertEqual(res_dict['backups'][0]['id'], backup_id1)
|
||||
self.assertEqual(res_dict['backups'][0]['name'], 'test_backup')
|
||||
self.assertEqual(len(res_dict['backups'][1]), 3)
|
||||
self.assertEqual(res_dict['backups'][1]['id'], backup_id2)
|
||||
self.assertEqual(res_dict['backups'][1]['name'], 'test_backup')
|
||||
self.assertEqual(len(res_dict['backups'][2]), 3)
|
||||
self.assertEqual(res_dict['backups'][2]['id'], backup_id3)
|
||||
self.assertEqual(res_dict['backups'][2]['name'], 'test_backup')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id3)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||
|
||||
def test_list_backups_xml(self):
|
||||
backup_id1 = self._create_backup()
|
||||
backup_id2 = self._create_backup()
|
||||
backup_id3 = self._create_backup()
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/xml'
|
||||
req.headers['Accept'] = 'application/xml'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
dom = minidom.parseString(res.body)
|
||||
backup_list = dom.getElementsByTagName('backup')
|
||||
|
||||
self.assertEqual(backup_list.item(0).attributes.length, 2)
|
||||
self.assertEqual(backup_list.item(0).getAttribute('id'),
|
||||
backup_id1)
|
||||
self.assertEqual(backup_list.item(1).attributes.length, 2)
|
||||
self.assertEqual(backup_list.item(1).getAttribute('id'),
|
||||
backup_id2)
|
||||
self.assertEqual(backup_list.item(2).attributes.length, 2)
|
||||
self.assertEqual(backup_list.item(2).getAttribute('id'),
|
||||
backup_id3)
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id3)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||
|
||||
def test_list_backups_detail_json(self):
|
||||
backup_id1 = self._create_backup()
|
||||
backup_id2 = self._create_backup()
|
||||
backup_id3 = self._create_backup()
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups/detail')
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.headers['Accept'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(len(res_dict['backups'][0]), 12)
|
||||
self.assertEqual(res_dict['backups'][0]['availability_zone'], 'az1')
|
||||
self.assertEqual(res_dict['backups'][0]['container'],
|
||||
'volumebackups')
|
||||
self.assertEqual(res_dict['backups'][0]['description'],
|
||||
'this is a test backup')
|
||||
self.assertEqual(res_dict['backups'][0]['name'],
|
||||
'test_backup')
|
||||
self.assertEqual(res_dict['backups'][0]['id'], backup_id1)
|
||||
self.assertEqual(res_dict['backups'][0]['object_count'], 0)
|
||||
self.assertEqual(res_dict['backups'][0]['size'], 0)
|
||||
self.assertEqual(res_dict['backups'][0]['status'], 'creating')
|
||||
self.assertEqual(res_dict['backups'][0]['volume_id'], '1')
|
||||
|
||||
self.assertEqual(len(res_dict['backups'][1]), 12)
|
||||
self.assertEqual(res_dict['backups'][1]['availability_zone'], 'az1')
|
||||
self.assertEqual(res_dict['backups'][1]['container'],
|
||||
'volumebackups')
|
||||
self.assertEqual(res_dict['backups'][1]['description'],
|
||||
'this is a test backup')
|
||||
self.assertEqual(res_dict['backups'][1]['name'],
|
||||
'test_backup')
|
||||
self.assertEqual(res_dict['backups'][1]['id'], backup_id2)
|
||||
self.assertEqual(res_dict['backups'][1]['object_count'], 0)
|
||||
self.assertEqual(res_dict['backups'][1]['size'], 0)
|
||||
self.assertEqual(res_dict['backups'][1]['status'], 'creating')
|
||||
self.assertEqual(res_dict['backups'][1]['volume_id'], '1')
|
||||
|
||||
self.assertEqual(len(res_dict['backups'][2]), 12)
|
||||
self.assertEqual(res_dict['backups'][2]['availability_zone'], 'az1')
|
||||
self.assertEqual(res_dict['backups'][2]['container'],
|
||||
'volumebackups')
|
||||
self.assertEqual(res_dict['backups'][2]['description'],
|
||||
'this is a test backup')
|
||||
self.assertEqual(res_dict['backups'][2]['name'],
|
||||
'test_backup')
|
||||
self.assertEqual(res_dict['backups'][2]['id'], backup_id3)
|
||||
self.assertEqual(res_dict['backups'][2]['object_count'], 0)
|
||||
self.assertEqual(res_dict['backups'][2]['size'], 0)
|
||||
self.assertEqual(res_dict['backups'][2]['status'], 'creating')
|
||||
self.assertEqual(res_dict['backups'][2]['volume_id'], '1')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id3)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||
|
||||
def test_list_backups_detail_xml(self):
|
||||
backup_id1 = self._create_backup()
|
||||
backup_id2 = self._create_backup()
|
||||
backup_id3 = self._create_backup()
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups/detail')
|
||||
req.method = 'GET'
|
||||
req.headers['Content-Type'] = 'application/xml'
|
||||
req.headers['Accept'] = 'application/xml'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
dom = minidom.parseString(res.body)
|
||||
backup_detail = dom.getElementsByTagName('backup')
|
||||
|
||||
self.assertEqual(backup_detail.item(0).attributes.length, 11)
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('availability_zone'), 'az1')
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('container'), 'volumebackups')
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('description'),
|
||||
'this is a test backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('name'), 'test_backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('id'), backup_id1)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(0).getAttribute('object_count')), 0)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(0).getAttribute('size')), 0)
|
||||
self.assertEqual(
|
||||
backup_detail.item(0).getAttribute('status'), 'creating')
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(0).getAttribute('volume_id')), 1)
|
||||
|
||||
self.assertEqual(backup_detail.item(1).attributes.length, 11)
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('availability_zone'), 'az1')
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('container'), 'volumebackups')
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('description'),
|
||||
'this is a test backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('name'), 'test_backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('id'), backup_id2)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(1).getAttribute('object_count')), 0)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(1).getAttribute('size')), 0)
|
||||
self.assertEqual(
|
||||
backup_detail.item(1).getAttribute('status'), 'creating')
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(1).getAttribute('volume_id')), 1)
|
||||
|
||||
self.assertEqual(backup_detail.item(2).attributes.length, 11)
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('availability_zone'), 'az1')
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('container'), 'volumebackups')
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('description'),
|
||||
'this is a test backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('name'), 'test_backup')
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('id'), backup_id3)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(2).getAttribute('object_count')), 0)
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(2).getAttribute('size')), 0)
|
||||
self.assertEqual(
|
||||
backup_detail.item(2).getAttribute('status'), 'creating')
|
||||
self.assertEqual(
|
||||
int(backup_detail.item(2).getAttribute('volume_id')), 1)
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id3)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id2)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id1)
|
||||
|
||||
def test_create_backup_json(self):
|
||||
volume_id = self._create_volume(status='available', size=5)
|
||||
body = {"backup": {"display_name": "nightly001",
|
||||
"display_description":
|
||||
"Nightly Backup 03-Sep-2012",
|
||||
"volume_id": volume_id,
|
||||
"container": "nightlybackups",
|
||||
}
|
||||
}
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
res_dict = json.loads(res.body)
|
||||
LOG.info(res_dict)
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertTrue('id' in res_dict['backup'])
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_create_backup_xml(self):
|
||||
volume_size = 2
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.body = ('<backup display_name="backup-001" '
|
||||
'display_description="Nightly Backup" '
|
||||
'volume_id="%s" container="Container001"/>' % volume_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/xml'
|
||||
req.headers['Accept'] = 'application/xml'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
dom = minidom.parseString(res.body)
|
||||
backup = dom.getElementsByTagName('backup')
|
||||
self.assertTrue(backup.item(0).hasAttribute('id'))
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_create_backup_with_no_body(self):
|
||||
# omit body from the request
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.body = json.dumps(None)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.headers['Accept'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'The server could not comply with the request since'
|
||||
' it is either malformed or otherwise incorrect.')
|
||||
|
||||
def test_create_backup_with_body_KeyError(self):
|
||||
# omit volume_id from body
|
||||
body = {"backup": {"display_name": "nightly001",
|
||||
"display_description":
|
||||
"Nightly Backup 03-Sep-2012",
|
||||
"container": "nightlybackups",
|
||||
}
|
||||
}
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Incorrect request body format')
|
||||
|
||||
def test_create_backup_with_VolumeNotFound(self):
|
||||
body = {"backup": {"display_name": "nightly001",
|
||||
"display_description":
|
||||
"Nightly Backup 03-Sep-2012",
|
||||
"volume_id": 9999,
|
||||
"container": "nightlybackups",
|
||||
}
|
||||
}
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['code'], 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['message'],
|
||||
'Volume 9999 could not be found.')
|
||||
|
||||
def test_create_backup_with_InvalidVolume(self):
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='restoring', size=volume_size)
|
||||
|
||||
body = {"backup": {"display_name": "nightly001",
|
||||
"display_description":
|
||||
"Nightly Backup 03-Sep-2012",
|
||||
"volume_id": volume_id,
|
||||
"container": "nightlybackups",
|
||||
}
|
||||
}
|
||||
req = webob.Request.blank('/v2/fake/backups')
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid volume: Volume to be backed up must'
|
||||
' be available')
|
||||
|
||||
def test_delete_backup_available(self):
|
||||
backup_id = self._create_backup(status='available')
|
||||
req = webob.Request.blank('/v2/fake/backups/%s' %
|
||||
backup_id)
|
||||
req.method = 'DELETE'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertEqual(self._get_backup_attrib(backup_id, 'status'),
|
||||
'deleting')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_delete_backup_error(self):
|
||||
backup_id = self._create_backup(status='error')
|
||||
req = webob.Request.blank('/v2/fake/backups/%s' %
|
||||
backup_id)
|
||||
req.method = 'DELETE'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertEqual(self._get_backup_attrib(backup_id, 'status'),
|
||||
'deleting')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_delete_backup_with_backup_NotFound(self):
|
||||
req = webob.Request.blank('/v2/fake/backups/9999')
|
||||
req.method = 'DELETE'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['code'], 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['message'],
|
||||
'Backup 9999 could not be found.')
|
||||
|
||||
def test_delete_backup_with_InvalidBackup(self):
|
||||
backup_id = self._create_backup()
|
||||
req = webob.Request.blank('/v2/fake/backups/%s' %
|
||||
backup_id)
|
||||
req.method = 'DELETE'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid backup: Backup status must be '
|
||||
'available or error')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_volume_id_specified_json(self):
|
||||
backup_id = self._create_backup(status='available')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertEqual(res_dict['restore']['backup_id'], backup_id)
|
||||
self.assertEqual(res_dict['restore']['volume_id'], volume_id)
|
||||
|
||||
def test_restore_backup_volume_id_specified_xml(self):
|
||||
backup_id = self._create_backup(status='available')
|
||||
volume_size = 2
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' % backup_id)
|
||||
req.body = '<restore volume_id="%s"/>' % volume_id
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/xml'
|
||||
req.headers['Accept'] = 'application/xml'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
dom = minidom.parseString(res.body)
|
||||
restore = dom.getElementsByTagName('restore')
|
||||
self.assertEqual(restore.item(0).getAttribute('backup_id'),
|
||||
backup_id)
|
||||
self.assertEqual(restore.item(0).getAttribute('volume_id'), volume_id)
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_restore_backup_with_no_body(self):
|
||||
# omit body from the request
|
||||
backup_id = self._create_backup(status='available')
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.body = json.dumps(None)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.headers['Accept'] = 'application/json'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'The server could not comply with the request since'
|
||||
' it is either malformed or otherwise incorrect.')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_with_body_KeyError(self):
|
||||
# omit restore from body
|
||||
backup_id = self._create_backup(status='available')
|
||||
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' % backup_id)
|
||||
body = {"": {}}
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.headers['Accept'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'The server could not comply with the request since'
|
||||
' it is either malformed or otherwise incorrect.')
|
||||
|
||||
def test_restore_backup_volume_id_unspecified(self):
|
||||
|
||||
# intercept volume creation to ensure created volume
|
||||
# has status of available
|
||||
def fake_volume_api_create(cls, context, size, name, description):
|
||||
volume_id = self._create_volume(status='available', size=size)
|
||||
return db.volume_get(context, volume_id)
|
||||
|
||||
self.stubs.Set(manila.volume.API, 'create',
|
||||
fake_volume_api_create)
|
||||
|
||||
backup_id = self._create_backup(size=5, status='available')
|
||||
|
||||
body = {"restore": {}}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertEqual(res_dict['restore']['backup_id'], backup_id)
|
||||
|
||||
def test_restore_backup_with_InvalidInput(self):
|
||||
|
||||
def fake_backup_api_restore_throwing_InvalidInput(cls, context,
|
||||
backup_id,
|
||||
volume_id):
|
||||
msg = _("Invalid input")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
self.stubs.Set(manila.backup.API, 'restore',
|
||||
fake_backup_api_restore_throwing_InvalidInput)
|
||||
|
||||
backup_id = self._create_backup(status='available')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 0
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid input received: Invalid input')
|
||||
|
||||
def test_restore_backup_with_InvalidVolume(self):
|
||||
backup_id = self._create_backup(status='available')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='attaching', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid volume: Volume to be restored to must '
|
||||
'be available')
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_with_InvalidBackup(self):
|
||||
backup_id = self._create_backup(status='restoring')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid backup: Backup status must be available')
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_with_BackupNotFound(self):
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/9999/restore')
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['code'], 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['message'],
|
||||
'Backup 9999 could not be found.')
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
|
||||
def test_restore_backup_with_VolumeNotFound(self):
|
||||
backup_id = self._create_backup(status='available')
|
||||
|
||||
body = {"restore": {"volume_id": "9999", }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['code'], 404)
|
||||
self.assertEqual(res_dict['itemNotFound']['message'],
|
||||
'Volume 9999 could not be found.')
|
||||
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_with_VolumeSizeExceedsAvailableQuota(self):
|
||||
|
||||
def fake_backup_api_restore_throwing_VolumeSizeExceedsAvailableQuota(
|
||||
cls, context, backup_id, volume_id):
|
||||
raise exception.VolumeSizeExceedsAvailableQuota()
|
||||
|
||||
self.stubs.Set(
|
||||
manila.backup.API,
|
||||
'restore',
|
||||
fake_backup_api_restore_throwing_VolumeSizeExceedsAvailableQuota)
|
||||
|
||||
backup_id = self._create_backup(status='available')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 413)
|
||||
self.assertEqual(res_dict['overLimit']['code'], 413)
|
||||
self.assertEqual(res_dict['overLimit']['message'],
|
||||
'Requested volume or snapshot exceeds allowed '
|
||||
'Gigabytes quota')
|
||||
|
||||
def test_restore_backup_with_VolumeLimitExceeded(self):
|
||||
|
||||
def fake_backup_api_restore_throwing_VolumeLimitExceeded(cls,
|
||||
context,
|
||||
backup_id,
|
||||
volume_id):
|
||||
raise exception.VolumeLimitExceeded(allowed=1)
|
||||
|
||||
self.stubs.Set(manila.backup.API, 'restore',
|
||||
fake_backup_api_restore_throwing_VolumeLimitExceeded)
|
||||
|
||||
backup_id = self._create_backup(status='available')
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 413)
|
||||
self.assertEqual(res_dict['overLimit']['code'], 413)
|
||||
self.assertEqual(res_dict['overLimit']['message'],
|
||||
'Maximum number of volumes allowed '
|
||||
'(%(allowed)d) exceeded')
|
||||
|
||||
def test_restore_backup_to_undersized_volume(self):
|
||||
backup_size = 10
|
||||
backup_id = self._create_backup(status='available', size=backup_size)
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 5
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 400)
|
||||
self.assertEqual(res_dict['badRequest']['code'], 400)
|
||||
self.assertEqual(res_dict['badRequest']['message'],
|
||||
'Invalid volume: volume size %d is too '
|
||||
'small to restore backup of size %d.'
|
||||
% (volume_size, backup_size))
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
||||
|
||||
def test_restore_backup_to_oversized_volume(self):
|
||||
backup_id = self._create_backup(status='available', size=10)
|
||||
# need to create the volume referenced below first
|
||||
volume_size = 15
|
||||
volume_id = self._create_volume(status='available', size=volume_size)
|
||||
|
||||
body = {"restore": {"volume_id": volume_id, }}
|
||||
req = webob.Request.blank('/v2/fake/backups/%s/restore' %
|
||||
backup_id)
|
||||
req.method = 'POST'
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = json.dumps(body)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(res.status_int, 202)
|
||||
self.assertEqual(res_dict['restore']['backup_id'], backup_id)
|
||||
self.assertEqual(res_dict['restore']['volume_id'], volume_id)
|
||||
|
||||
db.volume_destroy(context.get_admin_context(), volume_id)
|
||||
db.backup_destroy(context.get_admin_context(), backup_id)
|
|
@ -1,124 +0,0 @@
|
|||
# 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 lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import extended_snapshot_attributes
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila import volume
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
UUID1 = '00000000-0000-0000-0000-000000000001'
|
||||
UUID2 = '00000000-0000-0000-0000-000000000002'
|
||||
|
||||
|
||||
def _get_default_snapshot_param():
|
||||
return {'id': UUID1,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'volume_size': 100,
|
||||
'created_at': None,
|
||||
'display_name': 'Default name',
|
||||
'display_description': 'Default description',
|
||||
'project_id': 'fake',
|
||||
'progress': '0%'}
|
||||
|
||||
|
||||
def fake_snapshot_get(self, context, snapshot_id):
|
||||
param = _get_default_snapshot_param()
|
||||
return param
|
||||
|
||||
|
||||
def fake_snapshot_get_all(self, context, search_opts=None):
|
||||
param = _get_default_snapshot_param()
|
||||
return [param]
|
||||
|
||||
|
||||
class ExtendedSnapshotAttributesTest(test.TestCase):
|
||||
content_type = 'application/json'
|
||||
prefix = 'os-extended-snapshot-attributes:'
|
||||
|
||||
def setUp(self):
|
||||
super(ExtendedSnapshotAttributesTest, self).setUp()
|
||||
self.stubs.Set(volume.api.API, 'get_snapshot', fake_snapshot_get)
|
||||
self.stubs.Set(volume.api.API, 'get_all_snapshots',
|
||||
fake_snapshot_get_all)
|
||||
|
||||
def _make_request(self, url):
|
||||
req = webob.Request.blank(url)
|
||||
req.headers['Accept'] = self.content_type
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
return res
|
||||
|
||||
def _get_snapshot(self, body):
|
||||
return jsonutils.loads(body).get('snapshot')
|
||||
|
||||
def _get_snapshots(self, body):
|
||||
return jsonutils.loads(body).get('snapshots')
|
||||
|
||||
def assertSnapshotAttributes(self, snapshot, project_id, progress):
|
||||
self.assertEqual(snapshot.get('%sproject_id' % self.prefix),
|
||||
project_id)
|
||||
self.assertEqual(snapshot.get('%sprogress' % self.prefix), progress)
|
||||
|
||||
def test_show(self):
|
||||
url = '/v2/fake/snapshots/%s' % UUID2
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertSnapshotAttributes(self._get_snapshot(res.body),
|
||||
project_id='fake',
|
||||
progress='0%')
|
||||
|
||||
def test_detail(self):
|
||||
url = '/v2/fake/snapshots/detail'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
for i, snapshot in enumerate(self._get_snapshots(res.body)):
|
||||
self.assertSnapshotAttributes(snapshot,
|
||||
project_id='fake',
|
||||
progress='0%')
|
||||
|
||||
def test_no_instance_passthrough_404(self):
|
||||
|
||||
def fake_snapshot_get(*args, **kwargs):
|
||||
raise exception.InstanceNotFound(instance_id='fake')
|
||||
|
||||
self.stubs.Set(volume.api.API, 'get_snapshot', fake_snapshot_get)
|
||||
url = '/v2/fake/snapshots/70f6db34-de8d-4fbd-aafb-4065bdfa6115'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 404)
|
||||
|
||||
|
||||
class ExtendedSnapshotAttributesXmlTest(ExtendedSnapshotAttributesTest):
|
||||
content_type = 'application/xml'
|
||||
ext = extended_snapshot_attributes
|
||||
prefix = '{%s}' % ext.Extended_snapshot_attributes.namespace
|
||||
|
||||
def _get_snapshot(self, body):
|
||||
return etree.XML(body)
|
||||
|
||||
def _get_snapshots(self, body):
|
||||
return etree.XML(body).getchildren()
|
|
@ -1,232 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Zadara Storage Inc.
|
||||
# Copyright (c) 2011 OpenStack LLC.
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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 lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import types_extra_specs
|
||||
from manila.openstack.common.notifier import api as notifier_api
|
||||
from manila.openstack.common.notifier import test_notifier
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
import manila.wsgi
|
||||
|
||||
|
||||
def return_create_volume_type_extra_specs(context, volume_type_id,
|
||||
extra_specs):
|
||||
return stub_volume_type_extra_specs()
|
||||
|
||||
|
||||
def return_volume_type_extra_specs(context, volume_type_id):
|
||||
return stub_volume_type_extra_specs()
|
||||
|
||||
|
||||
def return_empty_volume_type_extra_specs(context, volume_type_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_volume_type_extra_specs(context, volume_type_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_volume_type_extra_specs():
|
||||
specs = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"}
|
||||
return specs
|
||||
|
||||
|
||||
def volume_type_get(context, volume_type_id):
|
||||
pass
|
||||
|
||||
|
||||
class VolumeTypesExtraSpecsTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeTypesExtraSpecsTest, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
host='fake',
|
||||
notification_driver=[test_notifier.__name__])
|
||||
self.stubs.Set(manila.db, 'volume_type_get', volume_type_get)
|
||||
self.api_path = '/v2/fake/os-volume-types/1/extra_specs'
|
||||
self.controller = types_extra_specs.VolumeTypeExtraSpecsController()
|
||||
"""to reset notifier drivers left over from other api/contrib tests"""
|
||||
notifier_api._reset_drivers()
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
def tearDown(self):
|
||||
notifier_api._reset_drivers()
|
||||
super(VolumeTypesExtraSpecsTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
self.stubs.Set(manila.db, 'volume_type_extra_specs_get',
|
||||
return_volume_type_extra_specs)
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path)
|
||||
res_dict = self.controller.index(req, 1)
|
||||
|
||||
self.assertEqual('value1', res_dict['extra_specs']['key1'])
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(manila.db, 'volume_type_extra_specs_get',
|
||||
return_empty_volume_type_extra_specs)
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path)
|
||||
res_dict = self.controller.index(req, 1)
|
||||
|
||||
self.assertEqual(0, len(res_dict['extra_specs']))
|
||||
|
||||
def test_show(self):
|
||||
self.stubs.Set(manila.db, 'volume_type_extra_specs_get',
|
||||
return_volume_type_extra_specs)
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/key5')
|
||||
res_dict = self.controller.show(req, 1, 'key5')
|
||||
|
||||
self.assertEqual('value5', res_dict['key5'])
|
||||
|
||||
def test_show_spec_not_found(self):
|
||||
self.stubs.Set(manila.db, 'volume_type_extra_specs_get',
|
||||
return_empty_volume_type_extra_specs)
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/key6')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, 1, 'key6')
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(manila.db, 'volume_type_extra_specs_delete',
|
||||
delete_volume_type_extra_specs)
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/key5')
|
||||
self.controller.delete(req, 1, 'key5')
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(manila.db,
|
||||
'volume_type_extra_specs_update_or_create',
|
||||
return_create_volume_type_extra_specs)
|
||||
body = {"extra_specs": {"key1": "value1"}}
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
req = fakes.HTTPRequest.blank(self.api_path)
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
|
||||
self.assertEqual('value1', res_dict['extra_specs']['key1'])
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(manila.db,
|
||||
'volume_type_extra_specs_update_or_create',
|
||||
return_create_volume_type_extra_specs)
|
||||
body = {"key1": "value1"}
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/key1')
|
||||
res_dict = self.controller.update(req, 1, 'key1', body)
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
|
||||
self.assertEqual('value1', res_dict['key1'])
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(manila.db,
|
||||
'volume_type_extra_specs_update_or_create',
|
||||
return_create_volume_type_extra_specs)
|
||||
body = {"key1": "value1", "key2": "value2"}
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/key1')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
|
||||
req, 1, 'key1', body)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(manila.db,
|
||||
'volume_type_extra_specs_update_or_create',
|
||||
return_create_volume_type_extra_specs)
|
||||
body = {"key1": "value1"}
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.api_path + '/bad')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
|
||||
req, 1, 'bad', body)
|
||||
|
||||
def _extra_specs_empty_update(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1/extra_specs')
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, '1', body)
|
||||
|
||||
def test_update_no_body(self):
|
||||
self._extra_specs_empty_update(body=None)
|
||||
|
||||
def test_update_empty_body(self):
|
||||
self._extra_specs_empty_update(body={})
|
||||
|
||||
def _extra_specs_create_bad_body(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1/extra_specs')
|
||||
req.method = 'POST'
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, '1', body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._extra_specs_create_bad_body(body=None)
|
||||
|
||||
def test_create_missing_volume(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._extra_specs_create_bad_body(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'extra_specs': 'string'}
|
||||
self._extra_specs_create_bad_body(body=body)
|
||||
|
||||
|
||||
class VolumeTypeExtraSpecsSerializerTest(test.TestCase):
|
||||
def test_index_create_serializer(self):
|
||||
serializer = types_extra_specs.VolumeTypeExtraSpecsTemplate()
|
||||
|
||||
# Just getting some input data
|
||||
extra_specs = stub_volume_type_extra_specs()
|
||||
text = serializer.serialize(dict(extra_specs=extra_specs))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('extra_specs', tree.tag)
|
||||
self.assertEqual(len(extra_specs), len(tree))
|
||||
seen = set(extra_specs.keys())
|
||||
for child in tree:
|
||||
self.assertTrue(child.tag in seen)
|
||||
self.assertEqual(extra_specs[child.tag], child.text)
|
||||
seen.remove(child.tag)
|
||||
self.assertEqual(len(seen), 0)
|
||||
|
||||
def test_update_show_serializer(self):
|
||||
serializer = types_extra_specs.VolumeTypeExtraSpecTemplate()
|
||||
|
||||
exemplar = dict(key1='value1')
|
||||
text = serializer.serialize(exemplar)
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('key1', tree.tag)
|
||||
self.assertEqual('value1', tree.text)
|
||||
self.assertEqual(0, len(tree))
|
|
@ -1,129 +0,0 @@
|
|||
# Copyright 2011 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.
|
||||
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import types_manage
|
||||
from manila import exception
|
||||
from manila.openstack.common.notifier import api as notifier_api
|
||||
from manila.openstack.common.notifier import test_notifier
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.volume import volume_types
|
||||
|
||||
|
||||
def stub_volume_type(id):
|
||||
specs = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"}
|
||||
return dict(id=id, name='vol_type_%s' % str(id), extra_specs=specs)
|
||||
|
||||
|
||||
def return_volume_types_get_volume_type(context, id):
|
||||
if id == "777":
|
||||
raise exception.VolumeTypeNotFound(volume_type_id=id)
|
||||
return stub_volume_type(int(id))
|
||||
|
||||
|
||||
def return_volume_types_destroy(context, name):
|
||||
if name == "777":
|
||||
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
||||
pass
|
||||
|
||||
|
||||
def return_volume_types_create(context, name, specs):
|
||||
pass
|
||||
|
||||
|
||||
def return_volume_types_get_by_name(context, name):
|
||||
if name == "777":
|
||||
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
||||
return stub_volume_type(int(name.split("_")[2]))
|
||||
|
||||
|
||||
class VolumeTypesManageApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeTypesManageApiTest, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
host='fake',
|
||||
notification_driver=[test_notifier.__name__])
|
||||
self.controller = types_manage.VolumeTypesManageController()
|
||||
"""to reset notifier drivers left over from other api/contrib tests"""
|
||||
notifier_api._reset_drivers()
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
def tearDown(self):
|
||||
notifier_api._reset_drivers()
|
||||
super(VolumeTypesManageApiTest, self).tearDown()
|
||||
|
||||
def test_volume_types_delete(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
self.stubs.Set(volume_types, 'destroy',
|
||||
return_volume_types_destroy)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
self.controller._delete(req, 1)
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
|
||||
def test_volume_types_delete_not_found(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
self.stubs.Set(volume_types, 'destroy',
|
||||
return_volume_types_destroy)
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
||||
req, '777')
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(volume_types, 'create',
|
||||
return_volume_types_create)
|
||||
self.stubs.Set(volume_types, 'get_volume_type_by_name',
|
||||
return_volume_types_get_by_name)
|
||||
|
||||
body = {"volume_type": {"name": "vol_type_1",
|
||||
"extra_specs": {"key1": "value1"}}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 0)
|
||||
res_dict = self.controller._create(req, body)
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
self.assertEqual(1, len(res_dict))
|
||||
self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
|
||||
|
||||
def _create_volume_type_bad_body(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
req.method = 'POST'
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._create, req, body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._create_volume_type_bad_body(body=None)
|
||||
|
||||
def test_create_missing_volume(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._create_volume_type_bad_body(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'volume_type': 'string'}
|
||||
self._create_volume_type_bad_body(body=body)
|
|
@ -1,248 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import volume_actions
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila.openstack.common.rpc import common as rpc_common
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests.api.v2 import stubs
|
||||
from manila import volume
|
||||
from manila.volume import api as volume_api
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def fake_volume_api(*args, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
def fake_volume_get(*args, **kwargs):
|
||||
return {'id': 'fake', 'host': 'fake'}
|
||||
|
||||
|
||||
class VolumeActionsTest(test.TestCase):
|
||||
|
||||
_actions = ('os-detach', 'os-reserve', 'os-unreserve')
|
||||
|
||||
_methods = ('attach', 'detach', 'reserve_volume', 'unreserve_volume')
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeActionsTest, self).setUp()
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_api)
|
||||
self.UUID = uuid.uuid4()
|
||||
for _method in self._methods:
|
||||
self.stubs.Set(volume.API, _method, fake_volume_api)
|
||||
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_get)
|
||||
|
||||
def test_simple_api_actions(self):
|
||||
app = fakes.wsgi_app()
|
||||
for _action in self._actions:
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s/action' %
|
||||
self.UUID)
|
||||
req.method = 'POST'
|
||||
req.body = jsonutils.dumps({_action: None})
|
||||
req.content_type = 'application/json'
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(res.status_int, 202)
|
||||
|
||||
def test_initialize_connection(self):
|
||||
def fake_initialize_connection(*args, **kwargs):
|
||||
return {}
|
||||
self.stubs.Set(volume.API, 'initialize_connection',
|
||||
fake_initialize_connection)
|
||||
|
||||
body = {'os-initialize_connection': {'connector': 'fake'}}
|
||||
req = webob.Request.blank('/v2/fake/volumes/1/action')
|
||||
req.method = "POST"
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
|
||||
def test_terminate_connection(self):
|
||||
def fake_terminate_connection(*args, **kwargs):
|
||||
return {}
|
||||
self.stubs.Set(volume.API, 'terminate_connection',
|
||||
fake_terminate_connection)
|
||||
|
||||
body = {'os-terminate_connection': {'connector': 'fake'}}
|
||||
req = webob.Request.blank('/v2/fake/volumes/1/action')
|
||||
req.method = "POST"
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 202)
|
||||
|
||||
def test_attach(self):
|
||||
body = {'os-attach': {'instance_uuid': 'fake',
|
||||
'mountpoint': '/dev/vdc'}}
|
||||
req = webob.Request.blank('/v2/fake/volumes/1/action')
|
||||
req.method = "POST"
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 202)
|
||||
|
||||
|
||||
def stub_volume_get(self, context, volume_id):
|
||||
volume = stubs.stub_volume(volume_id)
|
||||
if volume_id == 5:
|
||||
volume['status'] = 'in-use'
|
||||
else:
|
||||
volume['status'] = 'available'
|
||||
return volume
|
||||
|
||||
|
||||
def stub_upload_volume_to_image_service(self, context, volume, metadata,
|
||||
force):
|
||||
ret = {"id": volume['id'],
|
||||
"updated_at": datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
"status": 'uploading',
|
||||
"display_description": volume['display_description'],
|
||||
"size": volume['size'],
|
||||
"volume_type": volume['volume_type'],
|
||||
"image_id": 1,
|
||||
"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name'}
|
||||
return ret
|
||||
|
||||
|
||||
class VolumeImageActionsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeImageActionsTest, self).setUp()
|
||||
self.controller = volume_actions.VolumeActionsController()
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
|
||||
|
||||
def test_copy_volume_to_image(self):
|
||||
self.stubs.Set(volume_api.API,
|
||||
"copy_volume_to_image",
|
||||
stub_upload_volume_to_image_service)
|
||||
|
||||
id = 1
|
||||
vol = {"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name',
|
||||
"force": True}
|
||||
body = {"os-volume_upload_image": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/tenant1/volumes/%s/action' % id)
|
||||
res_dict = self.controller._volume_upload_image(req, id, body)
|
||||
expected = {'os-volume_upload_image': {'id': id,
|
||||
'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'status': 'uploading',
|
||||
'display_description': 'displaydesc',
|
||||
'size': 1,
|
||||
'volume_type': {'name': 'vol_type_name'},
|
||||
'image_id': 1,
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'raw',
|
||||
'image_name': 'image_name'}}
|
||||
self.assertDictMatch(res_dict, expected)
|
||||
|
||||
def test_copy_volume_to_image_volumenotfound(self):
|
||||
def stub_volume_get_raise_exc(self, context, volume_id):
|
||||
raise exception.VolumeNotFound(volume_id=volume_id)
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get', stub_volume_get_raise_exc)
|
||||
|
||||
id = 1
|
||||
vol = {"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name',
|
||||
"force": True}
|
||||
body = {"os-volume_upload_image": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/tenant1/volumes/%s/action' % id)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller._volume_upload_image,
|
||||
req,
|
||||
id,
|
||||
body)
|
||||
|
||||
def test_copy_volume_to_image_invalidvolume(self):
|
||||
def stub_upload_volume_to_image_service_raise(self, context, volume,
|
||||
metadata, force):
|
||||
raise exception.InvalidVolume(reason='blah')
|
||||
self.stubs.Set(volume_api.API,
|
||||
"copy_volume_to_image",
|
||||
stub_upload_volume_to_image_service_raise)
|
||||
|
||||
id = 1
|
||||
vol = {"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name',
|
||||
"force": True}
|
||||
body = {"os-volume_upload_image": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/tenant1/volumes/%s/action' % id)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._volume_upload_image,
|
||||
req,
|
||||
id,
|
||||
body)
|
||||
|
||||
def test_copy_volume_to_image_valueerror(self):
|
||||
def stub_upload_volume_to_image_service_raise(self, context, volume,
|
||||
metadata, force):
|
||||
raise ValueError
|
||||
self.stubs.Set(volume_api.API,
|
||||
"copy_volume_to_image",
|
||||
stub_upload_volume_to_image_service_raise)
|
||||
|
||||
id = 1
|
||||
vol = {"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name',
|
||||
"force": True}
|
||||
body = {"os-volume_upload_image": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/tenant1/volumes/%s/action' % id)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._volume_upload_image,
|
||||
req,
|
||||
id,
|
||||
body)
|
||||
|
||||
def test_copy_volume_to_image_remoteerror(self):
|
||||
def stub_upload_volume_to_image_service_raise(self, context, volume,
|
||||
metadata, force):
|
||||
raise rpc_common.RemoteError
|
||||
self.stubs.Set(volume_api.API,
|
||||
"copy_volume_to_image",
|
||||
stub_upload_volume_to_image_service_raise)
|
||||
|
||||
id = 1
|
||||
vol = {"container_format": 'bare',
|
||||
"disk_format": 'raw',
|
||||
"image_name": 'image_name',
|
||||
"force": True}
|
||||
body = {"os-volume_upload_image": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/tenant1/volumes/%s/action' % id)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._volume_upload_image,
|
||||
req,
|
||||
id,
|
||||
body)
|
|
@ -1,134 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila import context
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila import volume
|
||||
|
||||
|
||||
def fake_volume_get(*args, **kwargs):
|
||||
return {
|
||||
'id': 'fake',
|
||||
'host': 'host001',
|
||||
'status': 'available',
|
||||
'size': 5,
|
||||
'availability_zone': 'somewhere',
|
||||
'created_at': datetime.datetime.now(),
|
||||
'attach_status': None,
|
||||
'display_name': 'anothervolume',
|
||||
'display_description': 'Just another volume!',
|
||||
'volume_type_id': None,
|
||||
'snapshot_id': None,
|
||||
'project_id': 'fake',
|
||||
}
|
||||
|
||||
|
||||
def fake_volume_get_all(*args, **kwargs):
|
||||
return [fake_volume_get()]
|
||||
|
||||
|
||||
def app():
|
||||
# no auth, just let environ['manila.context'] pass through
|
||||
api = fakes.router.APIRouter()
|
||||
mapper = fakes.urlmap.URLMap()
|
||||
mapper['/v2'] = api
|
||||
return mapper
|
||||
|
||||
|
||||
class VolumeHostAttributeTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeHostAttributeTest, self).setUp()
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_get)
|
||||
self.stubs.Set(volume.API, 'get_all', fake_volume_get_all)
|
||||
self.UUID = uuid.uuid4()
|
||||
|
||||
def test_get_volume_allowed(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volume']
|
||||
self.assertEqual(vol['os-vol-host-attr:host'], 'host001')
|
||||
|
||||
def test_get_volume_unallowed(self):
|
||||
ctx = context.RequestContext('non-admin', 'fake', False)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volume']
|
||||
self.assertFalse('os-vol-host-attr:host' in vol)
|
||||
|
||||
def test_list_detail_volumes_allowed(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertEqual(vol[0]['os-vol-host-attr:host'], 'host001')
|
||||
|
||||
def test_list_detail_volumes_unallowed(self):
|
||||
ctx = context.RequestContext('non-admin', 'fake', False)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertFalse('os-vol-host-attr:host' in vol[0])
|
||||
|
||||
def test_list_simple_volumes_no_host(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertFalse('os-vol-host-attr:host' in vol[0])
|
||||
|
||||
def test_get_volume_xml(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.accept = 'application/xml'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = etree.XML(res.body)
|
||||
host_key = ('{http://docs.openstack.org/volume/ext/'
|
||||
'volume_host_attribute/api/v1}host')
|
||||
self.assertEqual(vol.get(host_key), 'host001')
|
||||
|
||||
def test_list_volumes_detail_xml(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.accept = 'application/xml'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = list(etree.XML(res.body))[0]
|
||||
host_key = ('{http://docs.openstack.org/volume/ext/'
|
||||
'volume_host_attribute/api/v1}host')
|
||||
self.assertEqual(vol.get(host_key), 'host001')
|
|
@ -1,130 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import uuid
|
||||
from xml.dom import minidom
|
||||
|
||||
import webob
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack.wsgi import MetadataXMLDeserializer
|
||||
from manila.api.openstack.wsgi import XMLDeserializer
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila import volume
|
||||
|
||||
|
||||
def fake_volume_get(*args, **kwargs):
|
||||
return {
|
||||
'id': 'fake',
|
||||
'host': 'host001',
|
||||
'status': 'available',
|
||||
'size': 5,
|
||||
'availability_zone': 'somewhere',
|
||||
'created_at': datetime.datetime.now(),
|
||||
'attach_status': None,
|
||||
'display_name': 'anothervolume',
|
||||
'display_description': 'Just another volume!',
|
||||
'volume_type_id': None,
|
||||
'snapshot_id': None,
|
||||
'project_id': 'fake',
|
||||
}
|
||||
|
||||
|
||||
def fake_volume_get_all(*args, **kwargs):
|
||||
return [fake_volume_get()]
|
||||
|
||||
|
||||
fake_image_metadata = {
|
||||
'image_id': 'someid',
|
||||
'image_name': 'fake',
|
||||
'kernel_id': 'somekernel',
|
||||
'ramdisk_id': 'someramdisk',
|
||||
}
|
||||
|
||||
|
||||
def fake_get_volume_image_metadata(*args, **kwargs):
|
||||
return fake_image_metadata
|
||||
|
||||
|
||||
class VolumeImageMetadataTest(test.TestCase):
|
||||
content_type = 'application/json'
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeImageMetadataTest, self).setUp()
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_get)
|
||||
self.stubs.Set(volume.API, 'get_all', fake_volume_get_all)
|
||||
self.stubs.Set(volume.API, 'get_volume_image_metadata',
|
||||
fake_get_volume_image_metadata)
|
||||
self.UUID = uuid.uuid4()
|
||||
|
||||
def _make_request(self, url):
|
||||
req = webob.Request.blank(url)
|
||||
req.accept = self.content_type
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
return res
|
||||
|
||||
def _get_image_metadata(self, body):
|
||||
return json.loads(body)['volume']['volume_image_metadata']
|
||||
|
||||
def _get_image_metadata_list(self, body):
|
||||
return [
|
||||
volume['volume_image_metadata']
|
||||
for volume in json.loads(body)['volumes']
|
||||
]
|
||||
|
||||
def test_get_volume(self):
|
||||
res = self._make_request('/v2/fake/volumes/%s' % self.UUID)
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(self._get_image_metadata(res.body),
|
||||
fake_image_metadata)
|
||||
|
||||
def test_list_detail_volumes(self):
|
||||
res = self._make_request('/v2/fake/volumes/detail')
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(self._get_image_metadata_list(res.body)[0],
|
||||
fake_image_metadata)
|
||||
|
||||
|
||||
class ImageMetadataXMLDeserializer(common.MetadataXMLDeserializer):
|
||||
metadata_node_name = "volume_image_metadata"
|
||||
|
||||
|
||||
class VolumeImageMetadataXMLTest(VolumeImageMetadataTest):
|
||||
content_type = 'application/xml'
|
||||
|
||||
def _get_image_metadata(self, body):
|
||||
deserializer = XMLDeserializer()
|
||||
volume = deserializer.find_first_child_named(
|
||||
minidom.parseString(body), 'volume')
|
||||
image_metadata = deserializer.find_first_child_named(
|
||||
volume, 'volume_image_metadata')
|
||||
return MetadataXMLDeserializer().extract_metadata(image_metadata)
|
||||
|
||||
def _get_image_metadata_list(self, body):
|
||||
deserializer = XMLDeserializer()
|
||||
volumes = deserializer.find_first_child_named(
|
||||
minidom.parseString(body), 'volumes')
|
||||
volume_list = deserializer.find_children_named(volumes, 'volume')
|
||||
image_metadata_list = [
|
||||
deserializer.find_first_child_named(
|
||||
volume, 'volume_image_metadata'
|
||||
)
|
||||
for volume in volume_list]
|
||||
return map(MetadataXMLDeserializer().extract_metadata,
|
||||
image_metadata_list)
|
|
@ -1,137 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila import context
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila import volume
|
||||
|
||||
|
||||
PROJECT_ID = '88fd1da4-f464-4a87-9ce5-26f2f40743b9'
|
||||
|
||||
|
||||
def fake_volume_get(*args, **kwargs):
|
||||
return {
|
||||
'id': 'fake',
|
||||
'host': 'host001',
|
||||
'status': 'available',
|
||||
'size': 5,
|
||||
'availability_zone': 'somewhere',
|
||||
'created_at': datetime.datetime.now(),
|
||||
'attach_status': None,
|
||||
'display_name': 'anothervolume',
|
||||
'display_description': 'Just another volume!',
|
||||
'volume_type_id': None,
|
||||
'snapshot_id': None,
|
||||
'project_id': PROJECT_ID,
|
||||
}
|
||||
|
||||
|
||||
def fake_volume_get_all(*args, **kwargs):
|
||||
return [fake_volume_get()]
|
||||
|
||||
|
||||
def app():
|
||||
# no auth, just let environ['manila.context'] pass through
|
||||
api = fakes.router.APIRouter()
|
||||
mapper = fakes.urlmap.URLMap()
|
||||
mapper['/v2'] = api
|
||||
return mapper
|
||||
|
||||
|
||||
class VolumeTenantAttributeTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeTenantAttributeTest, self).setUp()
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_get)
|
||||
self.stubs.Set(volume.API, 'get_all', fake_volume_get_all)
|
||||
self.UUID = uuid.uuid4()
|
||||
|
||||
def test_get_volume_allowed(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volume']
|
||||
self.assertEqual(vol['os-vol-tenant-attr:tenant_id'], PROJECT_ID)
|
||||
|
||||
def test_get_volume_unallowed(self):
|
||||
ctx = context.RequestContext('non-admin', 'fake', False)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volume']
|
||||
self.assertFalse('os-vol-tenant-attr:tenant_id' in vol)
|
||||
|
||||
def test_list_detail_volumes_allowed(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertEqual(vol[0]['os-vol-tenant-attr:tenant_id'], PROJECT_ID)
|
||||
|
||||
def test_list_detail_volumes_unallowed(self):
|
||||
ctx = context.RequestContext('non-admin', 'fake', False)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertFalse('os-vol-tenant-attr:tenant_id' in vol[0])
|
||||
|
||||
def test_list_simple_volumes_no_tenant_id(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes')
|
||||
req.method = 'GET'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = json.loads(res.body)['volumes']
|
||||
self.assertFalse('os-vol-tenant-attr:tenant_id' in vol[0])
|
||||
|
||||
def test_get_volume_xml(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/%s' % self.UUID)
|
||||
req.method = 'GET'
|
||||
req.accept = 'application/xml'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = etree.XML(res.body)
|
||||
tenant_key = ('{http://docs.openstack.org/volume/ext/'
|
||||
'volume_tenant_attribute/api/v1}tenant_id')
|
||||
self.assertEqual(vol.get(tenant_key), PROJECT_ID)
|
||||
|
||||
def test_list_volumes_detail_xml(self):
|
||||
ctx = context.RequestContext('admin', 'fake', True)
|
||||
req = webob.Request.blank('/v2/fake/volumes/detail')
|
||||
req.method = 'GET'
|
||||
req.accept = 'application/xml'
|
||||
req.environ['manila.context'] = ctx
|
||||
res = req.get_response(app())
|
||||
vol = list(etree.XML(res.body))[0]
|
||||
tenant_key = ('{http://docs.openstack.org/volume/ext/'
|
||||
'volume_tenant_attribute/api/v1}tenant_id')
|
||||
self.assertEqual(vol.get(tenant_key), PROJECT_ID)
|
|
@ -1,458 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 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.
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo.config import cfg
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v1 import snapshot_metadata
|
||||
from manila.api.v1 import snapshots
|
||||
import manila.db
|
||||
from manila import exception
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def return_create_snapshot_metadata_max(context,
|
||||
snapshot_id,
|
||||
metadata,
|
||||
delete):
|
||||
return stub_max_snapshot_metadata()
|
||||
|
||||
|
||||
def return_create_snapshot_metadata(context, snapshot_id, metadata, delete):
|
||||
return stub_snapshot_metadata()
|
||||
|
||||
|
||||
def return_snapshot_metadata(context, snapshot_id):
|
||||
if not isinstance(snapshot_id, str) or not len(snapshot_id) == 36:
|
||||
msg = 'id %s must be a uuid in return snapshot metadata' % snapshot_id
|
||||
raise Exception(msg)
|
||||
return stub_snapshot_metadata()
|
||||
|
||||
|
||||
def return_empty_snapshot_metadata(context, snapshot_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_snapshot_metadata(context, snapshot_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_snapshot_metadata():
|
||||
metadata = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
return metadata
|
||||
|
||||
|
||||
def stub_max_snapshot_metadata():
|
||||
metadata = {"metadata": {}}
|
||||
for num in range(CONF.quota_metadata_items):
|
||||
metadata['metadata']['key%i' % num] = "blah"
|
||||
return metadata
|
||||
|
||||
|
||||
def return_snapshot(context, snapshot_id):
|
||||
return {'id': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
|
||||
'name': 'fake',
|
||||
'status': 'available',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_volume(context, volume_id):
|
||||
return {'id': 'fake-vol-id',
|
||||
'size': 100,
|
||||
'name': 'fake',
|
||||
'host': 'fake-host',
|
||||
'status': 'available',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_snapshot_nonexistent(context, snapshot_id):
|
||||
raise exception.SnapshotNotFound('bogus test message')
|
||||
|
||||
|
||||
def fake_update_snapshot_metadata(self, context, snapshot, diff):
|
||||
pass
|
||||
|
||||
|
||||
class SnapshotMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SnapshotMetaDataTest, self).setUp()
|
||||
self.volume_api = manila.volume.api.API()
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.stubs.Set(manila.db, 'volume_get', return_volume)
|
||||
self.stubs.Set(manila.db, 'snapshot_get', return_snapshot)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
|
||||
self.stubs.Set(self.volume_api, 'update_snapshot_metadata',
|
||||
fake_update_snapshot_metadata)
|
||||
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.snapshot_controller = snapshots.SnapshotsController(self.ext_mgr)
|
||||
self.controller = snapshot_metadata.Controller()
|
||||
self.id = str(uuid.uuid4())
|
||||
self.url = '/v1/fake/snapshots/%s/metadata' % self.id
|
||||
|
||||
snap = {"volume_size": 100,
|
||||
"volume_id": "fake-vol-id",
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1",
|
||||
"host": "fake-host",
|
||||
"metadata": {}}
|
||||
body = {"snapshot": snap}
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
self.snapshot_controller.create(req, body)
|
||||
|
||||
def test_index(self):
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
'key3': 'value3',
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_index_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.index, req, self.url)
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
expected = {'metadata': {}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show(self):
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
res_dict = self.controller.show(req, self.id, 'key2')
|
||||
expected = {'meta': {'key2': 'value2'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key2')
|
||||
|
||||
def test_show_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key6')
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_delete',
|
||||
delete_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
req.method = 'DELETE'
|
||||
res = self.controller.delete(req, self.id, 'key2')
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_delete_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key1')
|
||||
|
||||
def test_delete_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key6')
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshot_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
res_dict = self.controller.create(req, self.id, body)
|
||||
self.assertEqual(body, res_dict)
|
||||
|
||||
def test_create_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, None)
|
||||
|
||||
def test_create_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_create_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req, self.id, body)
|
||||
|
||||
def test_create_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshot_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_update_all(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key10': 'value10',
|
||||
'key99': 'value99',
|
||||
},
|
||||
}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_empty_container(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_malformed_container(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'meta': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_malformed_data(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': ['asdf']}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get', return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
body = {'metadata': {'key10': 'value10'}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update_all, req, '100', body)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res_dict = self.controller.update(req, self.id, 'key1', body)
|
||||
expected = {'meta': {'key1': 'value1'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_item_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/v1.1/fake/snapshots/asdf/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', None)
|
||||
|
||||
def test_update_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, '', body)
|
||||
|
||||
def test_update_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, ("a" * 260), body)
|
||||
|
||||
def test_update_item_value_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": ("a" * 260)}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, "key1", body)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1", "key2": "value2"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/bad')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'bad', body)
|
||||
|
||||
def test_invalid_metadata_items_on_create(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
#test for long key
|
||||
data = {"metadata": {"a" * 260: "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for long value
|
||||
data = {"metadata": {"key": "v" * 260}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for empty key.
|
||||
data = {"metadata": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, data)
|
|
@ -1,417 +0,0 @@
|
|||
# Copyright 2011 Denali Systems, Inc.
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.v1 import snapshots
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests.api.v1 import stubs
|
||||
from manila import volume
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
UUID = '00000000-0000-0000-0000-000000000001'
|
||||
INVALID_UUID = '00000000-0000-0000-0000-000000000002'
|
||||
|
||||
|
||||
def _get_default_snapshot_param():
|
||||
return {'id': UUID,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'volume_size': 100,
|
||||
'created_at': None,
|
||||
'display_name': 'Default name',
|
||||
'display_description': 'Default description', }
|
||||
|
||||
|
||||
def stub_snapshot_create(self, context,
|
||||
volume_id, name,
|
||||
description, metadata):
|
||||
snapshot = _get_default_snapshot_param()
|
||||
snapshot['volume_id'] = volume_id
|
||||
snapshot['display_name'] = name
|
||||
snapshot['display_description'] = description
|
||||
snapshot['metadata'] = metadata
|
||||
return snapshot
|
||||
|
||||
|
||||
def stub_snapshot_delete(self, context, snapshot):
|
||||
if snapshot['id'] != UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
|
||||
def stub_snapshot_get(self, context, snapshot_id):
|
||||
if snapshot_id != UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
param = _get_default_snapshot_param()
|
||||
return param
|
||||
|
||||
|
||||
def stub_snapshot_get_all(self, context, search_opts=None):
|
||||
param = _get_default_snapshot_param()
|
||||
return [param]
|
||||
|
||||
|
||||
class SnapshotApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SnapshotApiTest, self).setUp()
|
||||
self.controller = snapshots.SnapshotsController()
|
||||
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stubs.stub_snapshot_get_all_by_project)
|
||||
self.stubs.Set(db, 'snapshot_get_all',
|
||||
stubs.stub_snapshot_get_all)
|
||||
|
||||
def test_snapshot_create(self):
|
||||
self.stubs.Set(volume.api.API, "create_snapshot", stub_snapshot_create)
|
||||
self.stubs.Set(volume.api.API, 'get', stubs.stub_volume_get)
|
||||
snapshot = {"volume_id": '12',
|
||||
"force": False,
|
||||
"display_name": "Snapshot Test Name",
|
||||
"display_description": "Snapshot Test Desc"}
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
resp_dict = self.controller.create(req, body)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['display_name'],
|
||||
snapshot['display_name'])
|
||||
self.assertEqual(resp_dict['snapshot']['display_description'],
|
||||
snapshot['display_description'])
|
||||
|
||||
def test_snapshot_create_force(self):
|
||||
self.stubs.Set(volume.api.API,
|
||||
"create_snapshot_force",
|
||||
stub_snapshot_create)
|
||||
self.stubs.Set(volume.api.API, 'get', stubs.stub_volume_get)
|
||||
snapshot = {"volume_id": '12',
|
||||
"force": True,
|
||||
"display_name": "Snapshot Test Name",
|
||||
"display_description": "Snapshot Test Desc"}
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
resp_dict = self.controller.create(req, body)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['display_name'],
|
||||
snapshot['display_name'])
|
||||
self.assertEqual(resp_dict['snapshot']['display_description'],
|
||||
snapshot['display_description'])
|
||||
|
||||
snapshot = {"volume_id": "12",
|
||||
"force": "**&&^^%%$$##@@",
|
||||
"display_name": "Snapshot Test Name",
|
||||
"display_description": "Snapshot Test Desc"}
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_snapshot_update(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
self.stubs.Set(volume.api.API, "update_snapshot",
|
||||
stubs.stub_snapshot_update)
|
||||
updates = {"display_name": "Updated Test Name", }
|
||||
body = {"snapshot": updates}
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % UUID)
|
||||
res_dict = self.controller.update(req, UUID, body)
|
||||
expected = {'snapshot': {
|
||||
'id': UUID,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'size': 100,
|
||||
'created_at': None,
|
||||
'display_name': 'Updated Test Name',
|
||||
'display_description': 'Default description',
|
||||
'metadata': {},
|
||||
}}
|
||||
self.assertEquals(expected, res_dict)
|
||||
|
||||
def test_snapshot_update_missing_body(self):
|
||||
body = {}
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % UUID)
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.update, req, UUID, body)
|
||||
|
||||
def test_snapshot_update_invalid_body(self):
|
||||
body = {'display_name': 'missing top level snapshot key'}
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % UUID)
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.update, req, UUID, body)
|
||||
|
||||
def test_snapshot_update_not_found(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
updates = {
|
||||
"display_name": "Updated Test Name",
|
||||
}
|
||||
body = {"snapshot": updates}
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/not-the-uuid')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.update, req,
|
||||
'not-the-uuid', body)
|
||||
|
||||
def test_snapshot_delete(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
self.stubs.Set(volume.api.API, "delete_snapshot", stub_snapshot_delete)
|
||||
|
||||
snapshot_id = UUID
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % snapshot_id)
|
||||
resp = self.controller.delete(req, snapshot_id)
|
||||
self.assertEqual(resp.status_int, 202)
|
||||
|
||||
def test_snapshot_delete_invalid_id(self):
|
||||
self.stubs.Set(volume.api.API, "delete_snapshot", stub_snapshot_delete)
|
||||
snapshot_id = INVALID_UUID
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % snapshot_id)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete,
|
||||
req,
|
||||
snapshot_id)
|
||||
|
||||
def test_snapshot_show(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % UUID)
|
||||
resp_dict = self.controller.show(req, UUID)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['id'], UUID)
|
||||
|
||||
def test_snapshot_show_invalid_id(self):
|
||||
snapshot_id = INVALID_UUID
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/%s' % snapshot_id)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
req,
|
||||
snapshot_id)
|
||||
|
||||
def test_snapshot_detail(self):
|
||||
self.stubs.Set(volume.api.API,
|
||||
"get_all_snapshots",
|
||||
stub_snapshot_get_all)
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots/detail')
|
||||
resp_dict = self.controller.detail(req)
|
||||
|
||||
self.assertTrue('snapshots' in resp_dict)
|
||||
resp_snapshots = resp_dict['snapshots']
|
||||
self.assertEqual(len(resp_snapshots), 1)
|
||||
|
||||
resp_snapshot = resp_snapshots.pop()
|
||||
self.assertEqual(resp_snapshot['id'], UUID)
|
||||
|
||||
def test_snapshot_list_by_status(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, display_name='backup1',
|
||||
status='available'),
|
||||
stubs.stub_snapshot(2, display_name='backup2',
|
||||
status='available'),
|
||||
stubs.stub_snapshot(3, display_name='backup3',
|
||||
status='creating'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# no status filter
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 3)
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?status=creating')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['status'], 'creating')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?status=available')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 2)
|
||||
for snapshot in resp['snapshots']:
|
||||
self.assertEquals(snapshot['status'], 'available')
|
||||
# no match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?status=error')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 0)
|
||||
|
||||
def test_snapshot_list_by_volume(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, volume_id='vol1', status='creating'),
|
||||
stubs.stub_snapshot(2, volume_id='vol1', status='available'),
|
||||
stubs.stub_snapshot(3, volume_id='vol2', status='available'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?volume_id=vol2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['volume_id'], 'vol2')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?volume_id=vol1')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 2)
|
||||
for snapshot in resp['snapshots']:
|
||||
self.assertEqual(snapshot['volume_id'], 'vol1')
|
||||
# multiple filters
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?volume_id=vol1'
|
||||
'&status=available')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['volume_id'], 'vol1')
|
||||
self.assertEqual(resp['snapshots'][0]['status'], 'available')
|
||||
|
||||
def test_snapshot_list_by_name(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, display_name='backup1'),
|
||||
stubs.stub_snapshot(2, display_name='backup2'),
|
||||
stubs.stub_snapshot(3, display_name='backup3'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# no display_name filter
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 3)
|
||||
# filter by one name
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?display_name=backup2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEquals(resp['snapshots'][0]['display_name'], 'backup2')
|
||||
# filter no match
|
||||
req = fakes.HTTPRequest.blank('/v1/snapshots?display_name=backup4')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 0)
|
||||
|
||||
def test_admin_list_snapshots_limited_to_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/snapshots',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
def test_admin_list_snapshots_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/snapshots?all_tenants=1',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(3, len(res['snapshots']))
|
||||
|
||||
def test_all_tenants_non_admin_gets_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/snapshots?all_tenants=1')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
def test_non_admin_get_by_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/snapshots')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
|
||||
class SnapshotSerializerTest(test.TestCase):
|
||||
def _verify_snapshot(self, snap, tree):
|
||||
self.assertEqual(tree.tag, 'snapshot')
|
||||
|
||||
for attr in ('id', 'status', 'size', 'created_at',
|
||||
'display_name', 'display_description', 'volume_id'):
|
||||
self.assertEqual(str(snap[attr]), tree.get(attr))
|
||||
|
||||
def test_snapshot_show_create_serializer(self):
|
||||
serializer = snapshots.SnapshotTemplate()
|
||||
raw_snapshot = dict(
|
||||
id='snap_id',
|
||||
status='snap_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
display_name='snap_name',
|
||||
display_description='snap_desc',
|
||||
volume_id='vol_id', )
|
||||
text = serializer.serialize(dict(snapshot=raw_snapshot))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_snapshot(raw_snapshot, tree)
|
||||
|
||||
def test_snapshot_index_detail_serializer(self):
|
||||
serializer = snapshots.SnapshotsTemplate()
|
||||
raw_snapshots = [dict(id='snap1_id',
|
||||
status='snap1_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
display_name='snap1_name',
|
||||
display_description='snap1_desc',
|
||||
volume_id='vol1_id', ),
|
||||
dict(id='snap2_id',
|
||||
status='snap2_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
display_name='snap2_name',
|
||||
display_description='snap2_desc',
|
||||
volume_id='vol2_id', )]
|
||||
text = serializer.serialize(dict(snapshots=raw_snapshots))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('snapshots', tree.tag)
|
||||
self.assertEqual(len(raw_snapshots), len(tree))
|
||||
for idx, child in enumerate(tree):
|
||||
self._verify_snapshot(raw_snapshots[idx], child)
|
||||
|
||||
|
||||
class SnapshotsUnprocessableEntityTestCase(test.TestCase):
|
||||
|
||||
"""
|
||||
Tests of places we throw 422 Unprocessable Entity from
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(SnapshotsUnprocessableEntityTestCase, self).setUp()
|
||||
self.controller = snapshots.SnapshotsController()
|
||||
|
||||
def _unprocessable_snapshot_create(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots')
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.create, req, body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._unprocessable_snapshot_create(body=None)
|
||||
|
||||
def test_create_missing_snapshot(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._unprocessable_snapshot_create(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'snapshot': 'string'}
|
||||
self._unprocessable_snapshot_create(body=body)
|
|
@ -1,194 +0,0 @@
|
|||
# Copyright 2011 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 lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.v1 import types
|
||||
from manila.api.views import types as views_types
|
||||
from manila import exception
|
||||
from manila.openstack.common import timeutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.volume import volume_types
|
||||
|
||||
|
||||
def stub_volume_type(id):
|
||||
specs = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"}
|
||||
return dict(id=id, name='vol_type_%s' % str(id), extra_specs=specs)
|
||||
|
||||
|
||||
def return_volume_types_get_all_types(context):
|
||||
return dict(vol_type_1=stub_volume_type(1),
|
||||
vol_type_2=stub_volume_type(2),
|
||||
vol_type_3=stub_volume_type(3))
|
||||
|
||||
|
||||
def return_empty_volume_types_get_all_types(context):
|
||||
return {}
|
||||
|
||||
|
||||
def return_volume_types_get_volume_type(context, id):
|
||||
if id == "777":
|
||||
raise exception.VolumeTypeNotFound(volume_type_id=id)
|
||||
return stub_volume_type(int(id))
|
||||
|
||||
|
||||
def return_volume_types_get_by_name(context, name):
|
||||
if name == "777":
|
||||
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
||||
return stub_volume_type(int(name.split("_")[2]))
|
||||
|
||||
|
||||
class VolumeTypesApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeTypesApiTest, self).setUp()
|
||||
self.controller = types.VolumeTypesController()
|
||||
|
||||
def test_volume_types_index(self):
|
||||
self.stubs.Set(volume_types, 'get_all_types',
|
||||
return_volume_types_get_all_types)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types')
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(3, len(res_dict['volume_types']))
|
||||
|
||||
expected_names = ['vol_type_1', 'vol_type_2', 'vol_type_3']
|
||||
actual_names = map(lambda e: e['name'], res_dict['volume_types'])
|
||||
self.assertEqual(set(actual_names), set(expected_names))
|
||||
for entry in res_dict['volume_types']:
|
||||
self.assertEqual('value1', entry['extra_specs']['key1'])
|
||||
|
||||
def test_volume_types_index_no_data(self):
|
||||
self.stubs.Set(volume_types, 'get_all_types',
|
||||
return_empty_volume_types_get_all_types)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types')
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(0, len(res_dict['volume_types']))
|
||||
|
||||
def test_volume_types_show(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types/1')
|
||||
res_dict = self.controller.show(req, 1)
|
||||
|
||||
self.assertEqual(1, len(res_dict))
|
||||
self.assertEqual('1', res_dict['volume_type']['id'])
|
||||
self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
|
||||
|
||||
def test_volume_types_show_not_found(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types/777')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, '777')
|
||||
|
||||
def test_view_builder_show(self):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
|
||||
now = timeutils.isotime()
|
||||
raw_volume_type = dict(name='new_type',
|
||||
deleted=False,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
extra_specs={},
|
||||
deleted_at=None,
|
||||
id=42)
|
||||
|
||||
request = fakes.HTTPRequest.blank("/v1")
|
||||
output = view_builder.show(request, raw_volume_type)
|
||||
|
||||
self.assertTrue('volume_type' in output)
|
||||
expected_volume_type = dict(name='new_type',
|
||||
extra_specs={},
|
||||
id=42)
|
||||
self.assertDictMatch(output['volume_type'], expected_volume_type)
|
||||
|
||||
def test_view_builder_list(self):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
|
||||
now = timeutils.isotime()
|
||||
raw_volume_types = []
|
||||
for i in range(0, 10):
|
||||
raw_volume_types.append(dict(name='new_type',
|
||||
deleted=False,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
extra_specs={},
|
||||
deleted_at=None,
|
||||
id=42 + i))
|
||||
|
||||
request = fakes.HTTPRequest.blank("/v1")
|
||||
output = view_builder.index(request, raw_volume_types)
|
||||
|
||||
self.assertTrue('volume_types' in output)
|
||||
for i in range(0, 10):
|
||||
expected_volume_type = dict(name='new_type',
|
||||
extra_specs={},
|
||||
id=42 + i)
|
||||
self.assertDictMatch(output['volume_types'][i],
|
||||
expected_volume_type)
|
||||
|
||||
|
||||
class VolumeTypesSerializerTest(test.TestCase):
|
||||
def _verify_volume_type(self, vtype, tree):
|
||||
self.assertEqual('volume_type', tree.tag)
|
||||
self.assertEqual(vtype['name'], tree.get('name'))
|
||||
self.assertEqual(str(vtype['id']), tree.get('id'))
|
||||
self.assertEqual(1, len(tree))
|
||||
extra_specs = tree[0]
|
||||
self.assertEqual('extra_specs', extra_specs.tag)
|
||||
seen = set(vtype['extra_specs'].keys())
|
||||
for child in extra_specs:
|
||||
self.assertTrue(child.tag in seen)
|
||||
self.assertEqual(vtype['extra_specs'][child.tag], child.text)
|
||||
seen.remove(child.tag)
|
||||
self.assertEqual(len(seen), 0)
|
||||
|
||||
def test_index_serializer(self):
|
||||
serializer = types.VolumeTypesTemplate()
|
||||
|
||||
# Just getting some input data
|
||||
vtypes = return_volume_types_get_all_types(None)
|
||||
text = serializer.serialize({'volume_types': vtypes.values()})
|
||||
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('volume_types', tree.tag)
|
||||
self.assertEqual(len(vtypes), len(tree))
|
||||
for child in tree:
|
||||
name = child.get('name')
|
||||
self.assertTrue(name in vtypes)
|
||||
self._verify_volume_type(vtypes[name], child)
|
||||
|
||||
def test_voltype_serializer(self):
|
||||
serializer = types.VolumeTypeTemplate()
|
||||
|
||||
vtype = stub_volume_type(1)
|
||||
text = serializer.serialize(dict(volume_type=vtype))
|
||||
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_volume_type(vtype, tree)
|
|
@ -1,441 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 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.
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo.config import cfg
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v1 import volume_metadata
|
||||
from manila.api.v1 import volumes
|
||||
import manila.db
|
||||
from manila import exception
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def return_create_volume_metadata_max(context, volume_id, metadata, delete):
|
||||
return stub_max_volume_metadata()
|
||||
|
||||
|
||||
def return_create_volume_metadata(context, volume_id, metadata, delete):
|
||||
return stub_volume_metadata()
|
||||
|
||||
|
||||
def return_volume_metadata(context, volume_id):
|
||||
if not isinstance(volume_id, str) or not len(volume_id) == 36:
|
||||
msg = 'id %s must be a uuid in return volume metadata' % volume_id
|
||||
raise Exception(msg)
|
||||
return stub_volume_metadata()
|
||||
|
||||
|
||||
def return_empty_volume_metadata(context, volume_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_volume_metadata(context, volume_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_volume_metadata():
|
||||
metadata = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
return metadata
|
||||
|
||||
|
||||
def stub_max_volume_metadata():
|
||||
metadata = {"metadata": {}}
|
||||
for num in range(CONF.quota_metadata_items):
|
||||
metadata['metadata']['key%i' % num] = "blah"
|
||||
return metadata
|
||||
|
||||
|
||||
def return_volume(context, volume_id):
|
||||
return {'id': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
|
||||
'name': 'fake',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_volume_nonexistent(context, volume_id):
|
||||
raise exception.VolumeNotFound('bogus test message')
|
||||
|
||||
|
||||
def fake_update_volume_metadata(self, context, volume, diff):
|
||||
pass
|
||||
|
||||
|
||||
class volumeMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(volumeMetaDataTest, self).setUp()
|
||||
self.volume_api = manila.volume.api.API()
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.stubs.Set(manila.db, 'volume_get', return_volume)
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_volume_metadata)
|
||||
|
||||
self.stubs.Set(self.volume_api, 'update_volume_metadata',
|
||||
fake_update_volume_metadata)
|
||||
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.volume_controller = volumes.VolumeController(self.ext_mgr)
|
||||
self.controller = volume_metadata.Controller()
|
||||
self.id = str(uuid.uuid4())
|
||||
self.url = '/v1/fake/volumes/%s/metadata' % self.id
|
||||
|
||||
vol = {"size": 100,
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1",
|
||||
"metadata": {}}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
self.volume_controller.create(req, body)
|
||||
|
||||
def test_index(self):
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
'key3': 'value3',
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_index_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_volume_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.index, req, self.url)
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_empty_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
expected = {'metadata': {}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show(self):
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
res_dict = self.controller.show(req, self.id, 'key2')
|
||||
expected = {'meta': {'key2': 'value2'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_volume_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key2')
|
||||
|
||||
def test_show_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_empty_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key6')
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_volume_metadata)
|
||||
self.stubs.Set(manila.db, 'volume_metadata_delete',
|
||||
delete_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
req.method = 'DELETE'
|
||||
res = self.controller.delete(req, self.id, 'key2')
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_delete_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_get',
|
||||
return_volume_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key1')
|
||||
|
||||
def test_delete_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_empty_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key6')
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_empty_volume_metadata)
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volume_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
res_dict = self.controller.create(req, self.id, body)
|
||||
self.assertEqual(body, res_dict)
|
||||
|
||||
def test_create_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, None)
|
||||
|
||||
def test_create_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_create_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req, self.id, body)
|
||||
|
||||
def test_create_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_get',
|
||||
return_volume_nonexistent)
|
||||
self.stubs.Set(manila.db, 'volume_metadata_get',
|
||||
return_volume_metadata)
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volume_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_update_all(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key10': 'value10',
|
||||
'key99': 'value99',
|
||||
},
|
||||
}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_empty_container(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_malformed_container(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'meta': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_malformed_data(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': ['asdf']}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_get', return_volume_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
body = {'metadata': {'key10': 'value10'}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update_all, req, '100', body)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res_dict = self.controller.update(req, self.id, 'key1', body)
|
||||
expected = {'meta': {'key1': 'value1'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_item_nonexistent_volume(self):
|
||||
self.stubs.Set(manila.db, 'volume_get',
|
||||
return_volume_nonexistent)
|
||||
req = fakes.HTTPRequest.blank('/v1.1/fake/volumes/asdf/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', None)
|
||||
|
||||
def test_update_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, '', body)
|
||||
|
||||
def test_update_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, ("a" * 260), body)
|
||||
|
||||
def test_update_item_value_too_long(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": ("a" * 260)}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, "key1", body)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1", "key2": "value2"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/bad')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'bad', body)
|
||||
|
||||
def test_invalid_metadata_items_on_create(self):
|
||||
self.stubs.Set(manila.db, 'volume_metadata_update',
|
||||
return_create_volume_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
#test for long key
|
||||
data = {"metadata": {"a" * 260: "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for long value
|
||||
data = {"metadata": {"key": "v" * 260}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for empty key.
|
||||
data = {"metadata": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, data)
|
|
@ -1,764 +0,0 @@
|
|||
# Copyright 2013 Josh Durgin
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v1 import volumes
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests.api.v2 import stubs
|
||||
from manila.tests.image import fake as fake_image
|
||||
from manila.volume import api as volume_api
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
NS = '{http://docs.openstack.org/volume/api/v1}'
|
||||
|
||||
TEST_SNAPSHOT_UUID = '00000000-0000-0000-0000-000000000001'
|
||||
|
||||
|
||||
def stub_snapshot_get(self, context, snapshot_id):
|
||||
if snapshot_id != TEST_SNAPSHOT_UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
return {'id': snapshot_id,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'volume_size': 100,
|
||||
'created_at': None,
|
||||
'display_name': 'Default name',
|
||||
'display_description': 'Default description', }
|
||||
|
||||
|
||||
class VolumeApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeApiTest, self).setUp()
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
fake_image.stub_out_image_service(self.stubs)
|
||||
self.controller = volumes.VolumeController(self.ext_mgr)
|
||||
|
||||
self.stubs.Set(db, 'volume_get_all', stubs.stub_volume_get_all)
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
||||
self.stubs.Set(volume_api.API, 'delete', stubs.stub_volume_delete)
|
||||
|
||||
def test_volume_create(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
|
||||
vol = {"size": 100,
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1"}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
expected = {'volume': {'status': 'fakestatus',
|
||||
'display_description': 'Volume Test Desc',
|
||||
'availability_zone': 'zone1:host1',
|
||||
'display_name': 'Volume Test Name',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 100}}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_create_with_type(self):
|
||||
vol_type = FLAGS.default_volume_type
|
||||
db.volume_type_create(context.get_admin_context(),
|
||||
dict(name=vol_type, extra_specs={}))
|
||||
db_vol_type = db.volume_type_get_by_name(context.get_admin_context(),
|
||||
vol_type)
|
||||
|
||||
vol = {"size": 100,
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1",
|
||||
"volume_type": db_vol_type['name'], }
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
self.assertEquals(res_dict['volume']['volume_type'],
|
||||
db_vol_type['name'])
|
||||
|
||||
def test_volume_creation_fails_with_bad_size(self):
|
||||
vol = {"size": '',
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1"}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_create_with_image_id(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
test_id = "c905cedb-7281-47e4-8a62-f26bc5fc4c77"
|
||||
vol = {"size": '1',
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "nova",
|
||||
"imageRef": test_id}
|
||||
expected = {'volume': {'status': 'fakestatus',
|
||||
'display_description': 'Volume Test Desc',
|
||||
'availability_zone': 'nova',
|
||||
'display_name': 'Volume Test Name',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'image_id': test_id,
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': '1'}}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_create_with_image_id_is_integer(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
vol = {"size": '1',
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "manila",
|
||||
"imageRef": 1234}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_create_with_image_id_not_uuid_format(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
vol = {"size": '1',
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "manila",
|
||||
"imageRef": '12345'}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_update(self):
|
||||
self.stubs.Set(volume_api.API, "update", stubs.stub_volume_update)
|
||||
updates = {
|
||||
"display_name": "Updated Test Name",
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
res_dict = self.controller.update(req, '1', body)
|
||||
expected = {'volume': {
|
||||
'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'Updated Test Name',
|
||||
'attachments': [{
|
||||
'id': '1',
|
||||
'volume_id': '1',
|
||||
'server_id': 'fakeuuid',
|
||||
'device': '/',
|
||||
}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
}}
|
||||
self.assertEquals(res_dict, expected)
|
||||
|
||||
def test_volume_update_metadata(self):
|
||||
self.stubs.Set(volume_api.API, "update", stubs.stub_volume_update)
|
||||
updates = {
|
||||
"metadata": {"qos_max_iops": 2000}
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
res_dict = self.controller.update(req, '1', body)
|
||||
expected = {'volume': {
|
||||
'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [{
|
||||
'id': '1',
|
||||
'volume_id': '1',
|
||||
'server_id': 'fakeuuid',
|
||||
'device': '/',
|
||||
}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {"qos_max_iops": 2000},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
}}
|
||||
self.assertEquals(res_dict, expected)
|
||||
|
||||
def test_update_empty_body(self):
|
||||
body = {}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_update_invalid_body(self):
|
||||
body = {'display_name': 'missing top level volume key'}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_update_not_found(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
updates = {
|
||||
"display_name": "Updated Test Name",
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_volume_list(self):
|
||||
self.stubs.Set(volume_api.API, 'get_all',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
res_dict = self.controller.index(req)
|
||||
expected = {'volumes': [{'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 1}]}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_list_detail(self):
|
||||
self.stubs.Set(volume_api.API, 'get_all',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/detail')
|
||||
res_dict = self.controller.index(req)
|
||||
expected = {'volumes': [{'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 1}]}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_list_by_name(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
stubs.stub_volume(3, display_name='vol3'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
|
||||
# no display_name filter
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 3)
|
||||
# filter on display_name
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?display_name=vol2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['display_name'], 'vol2')
|
||||
# filter no match
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?display_name=vol4')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 0)
|
||||
|
||||
def test_volume_list_by_status(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1', status='available'),
|
||||
stubs.stub_volume(2, display_name='vol2', status='available'),
|
||||
stubs.stub_volume(3, display_name='vol3', status='in-use'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
# no status filter
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 3)
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?status=in-use')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['status'], 'in-use')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?status=available')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 2)
|
||||
for volume in resp['volumes']:
|
||||
self.assertEqual(volume['status'], 'available')
|
||||
# multiple filters
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?status=available&'
|
||||
'display_name=vol1')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['display_name'], 'vol1')
|
||||
self.assertEqual(resp['volumes'][0]['status'], 'available')
|
||||
# no match
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes?status=in-use&'
|
||||
'display_name=vol1')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 0)
|
||||
|
||||
def test_volume_show(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = {'volume': {'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 1}}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_show_no_attachments(self):
|
||||
def stub_volume_get(self, context, volume_id):
|
||||
return stubs.stub_volume(volume_id, attach_status='detached')
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = {'volume': {'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [],
|
||||
'bootable': 'false',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 1}}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_show_bootable(self):
|
||||
def stub_volume_get(self, context, volume_id):
|
||||
return (stubs.stub_volume(volume_id,
|
||||
volume_glance_metadata=dict(foo='bar')))
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = {'volume': {'status': 'fakestatus',
|
||||
'display_description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'display_name': 'displayname',
|
||||
'attachments': [{'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'}],
|
||||
'bootable': 'true',
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1,
|
||||
1, 1, 1),
|
||||
'size': 1}}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_show_no_volume(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show,
|
||||
req,
|
||||
1)
|
||||
|
||||
def test_volume_delete(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
resp = self.controller.delete(req, 1)
|
||||
self.assertEqual(resp.status_int, 202)
|
||||
|
||||
def test_volume_delete_no_volume(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v1/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete,
|
||||
req,
|
||||
1)
|
||||
|
||||
def test_admin_list_volumes_limited_to_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/volumes',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
def test_admin_list_volumes_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/volumes?all_tenants=1',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(3, len(res['volumes']))
|
||||
|
||||
def test_all_tenants_non_admin_gets_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/volumes?all_tenants=1')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
def test_non_admin_get_by_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/volumes')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
|
||||
class VolumeSerializerTest(test.TestCase):
|
||||
def _verify_volume_attachment(self, attach, tree):
|
||||
for attr in ('id', 'volume_id', 'server_id', 'device'):
|
||||
self.assertEqual(str(attach[attr]), tree.get(attr))
|
||||
|
||||
def _verify_volume(self, vol, tree):
|
||||
self.assertEqual(tree.tag, NS + 'volume')
|
||||
|
||||
for attr in ('id', 'status', 'size', 'availability_zone', 'created_at',
|
||||
'display_name', 'display_description', 'volume_type',
|
||||
'snapshot_id'):
|
||||
self.assertEqual(str(vol[attr]), tree.get(attr))
|
||||
|
||||
for child in tree:
|
||||
print child.tag
|
||||
self.assertTrue(child.tag in (NS + 'attachments', NS + 'metadata'))
|
||||
if child.tag == 'attachments':
|
||||
self.assertEqual(1, len(child))
|
||||
self.assertEqual('attachment', child[0].tag)
|
||||
self._verify_volume_attachment(vol['attachments'][0], child[0])
|
||||
elif child.tag == 'metadata':
|
||||
not_seen = set(vol['metadata'].keys())
|
||||
for gr_child in child:
|
||||
self.assertTrue(gr_child.get("key") in not_seen)
|
||||
self.assertEqual(str(vol['metadata'][gr_child.get("key")]),
|
||||
gr_child.text)
|
||||
not_seen.remove(gr_child.get('key'))
|
||||
self.assertEqual(0, len(not_seen))
|
||||
|
||||
def test_volume_show_create_serializer(self):
|
||||
serializer = volumes.VolumeTemplate()
|
||||
raw_volume = dict(
|
||||
id='vol_id',
|
||||
status='vol_status',
|
||||
size=1024,
|
||||
availability_zone='vol_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[dict(id='vol_id',
|
||||
volume_id='vol_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo')],
|
||||
display_name='vol_name',
|
||||
display_description='vol_desc',
|
||||
volume_type='vol_type',
|
||||
snapshot_id='snap_id',
|
||||
source_volid='source_volid',
|
||||
metadata=dict(foo='bar',
|
||||
baz='quux', ), )
|
||||
text = serializer.serialize(dict(volume=raw_volume))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_volume(raw_volume, tree)
|
||||
|
||||
def test_volume_index_detail_serializer(self):
|
||||
serializer = volumes.VolumesTemplate()
|
||||
raw_volumes = [dict(id='vol1_id',
|
||||
status='vol1_status',
|
||||
size=1024,
|
||||
availability_zone='vol1_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[dict(id='vol1_id',
|
||||
volume_id='vol1_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo1')],
|
||||
display_name='vol1_name',
|
||||
display_description='vol1_desc',
|
||||
volume_type='vol1_type',
|
||||
snapshot_id='snap1_id',
|
||||
source_volid=None,
|
||||
metadata=dict(foo='vol1_foo',
|
||||
bar='vol1_bar', ), ),
|
||||
dict(id='vol2_id',
|
||||
status='vol2_status',
|
||||
size=1024,
|
||||
availability_zone='vol2_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[dict(id='vol2_id',
|
||||
volume_id='vol2_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo2')],
|
||||
display_name='vol2_name',
|
||||
display_description='vol2_desc',
|
||||
volume_type='vol2_type',
|
||||
snapshot_id='snap2_id',
|
||||
source_volid=None,
|
||||
metadata=dict(foo='vol2_foo',
|
||||
bar='vol2_bar', ), )]
|
||||
text = serializer.serialize(dict(volumes=raw_volumes))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual(NS + 'volumes', tree.tag)
|
||||
self.assertEqual(len(raw_volumes), len(tree))
|
||||
for idx, child in enumerate(tree):
|
||||
self._verify_volume(raw_volumes[idx], child)
|
||||
|
||||
|
||||
class TestVolumeCreateRequestXMLDeserializer(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVolumeCreateRequestXMLDeserializer, self).setUp()
|
||||
self.deserializer = volumes.CreateDeserializer()
|
||||
|
||||
def test_minimal_volume(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {"volume": {"size": "1", }, }
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_display_name(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"
|
||||
display_name="Volume-xml"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"display_name": "Volume-xml",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_display_description(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"
|
||||
display_name="Volume-xml"
|
||||
display_description="description"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"display_name": "Volume-xml",
|
||||
"display_description": "description",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_volume_type(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"
|
||||
display_name="Volume-xml"
|
||||
display_description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"display_name": "Volume-xml",
|
||||
"size": "1",
|
||||
"display_name": "Volume-xml",
|
||||
"display_description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_availability_zone(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"
|
||||
display_name="Volume-xml"
|
||||
display_description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
|
||||
availability_zone="us-east1"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"display_name": "Volume-xml",
|
||||
"display_description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
"availability_zone": "us-east1",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_metadata(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
display_name="Volume-xml"
|
||||
size="1">
|
||||
<metadata><meta key="Type">work</meta></metadata></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"display_name": "Volume-xml",
|
||||
"size": "1",
|
||||
"metadata": {
|
||||
"Type": "work",
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_full_volume(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
size="1"
|
||||
display_name="Volume-xml"
|
||||
display_description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
|
||||
availability_zone="us-east1">
|
||||
<metadata><meta key="Type">work</meta></metadata></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"display_name": "Volume-xml",
|
||||
"display_description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
"availability_zone": "us-east1",
|
||||
"metadata": {
|
||||
"Type": "work",
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
|
||||
class VolumesUnprocessableEntityTestCase(test.TestCase):
|
||||
|
||||
"""
|
||||
Tests of places we throw 422 Unprocessable Entity from
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(VolumesUnprocessableEntityTestCase, self).setUp()
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.controller = volumes.VolumeController(self.ext_mgr)
|
||||
|
||||
def _unprocessable_volume_create(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes')
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPUnprocessableEntity,
|
||||
self.controller.create, req, body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._unprocessable_volume_create(body=None)
|
||||
|
||||
def test_create_missing_volume(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._unprocessable_volume_create(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'volume': 'string'}
|
||||
self._unprocessable_volume_create(body=body)
|
|
@ -1,458 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 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.
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo.config import cfg
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v2 import snapshot_metadata
|
||||
from manila.api.v2 import snapshots
|
||||
import manila.db
|
||||
from manila import exception
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def return_create_snapshot_metadata_max(context,
|
||||
snapshot_id,
|
||||
metadata,
|
||||
delete):
|
||||
return stub_max_snapshot_metadata()
|
||||
|
||||
|
||||
def return_create_snapshot_metadata(context, snapshot_id, metadata, delete):
|
||||
return stub_snapshot_metadata()
|
||||
|
||||
|
||||
def return_snapshot_metadata(context, snapshot_id):
|
||||
if not isinstance(snapshot_id, str) or not len(snapshot_id) == 36:
|
||||
msg = 'id %s must be a uuid in return snapshot metadata' % snapshot_id
|
||||
raise Exception(msg)
|
||||
return stub_snapshot_metadata()
|
||||
|
||||
|
||||
def return_empty_snapshot_metadata(context, snapshot_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_snapshot_metadata(context, snapshot_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_snapshot_metadata():
|
||||
metadata = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
}
|
||||
return metadata
|
||||
|
||||
|
||||
def stub_max_snapshot_metadata():
|
||||
metadata = {"metadata": {}}
|
||||
for num in range(CONF.quota_metadata_items):
|
||||
metadata['metadata']['key%i' % num] = "blah"
|
||||
return metadata
|
||||
|
||||
|
||||
def return_snapshot(context, snapshot_id):
|
||||
return {'id': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
|
||||
'name': 'fake',
|
||||
'status': 'available',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_volume(context, volume_id):
|
||||
return {'id': 'fake-vol-id',
|
||||
'size': 100,
|
||||
'name': 'fake',
|
||||
'host': 'fake-host',
|
||||
'status': 'available',
|
||||
'metadata': {}}
|
||||
|
||||
|
||||
def return_snapshot_nonexistent(context, snapshot_id):
|
||||
raise exception.SnapshotNotFound('bogus test message')
|
||||
|
||||
|
||||
def fake_update_snapshot_metadata(self, context, snapshot, diff):
|
||||
pass
|
||||
|
||||
|
||||
class SnapshotMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SnapshotMetaDataTest, self).setUp()
|
||||
self.volume_api = manila.volume.api.API()
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.stubs.Set(manila.db, 'volume_get', return_volume)
|
||||
self.stubs.Set(manila.db, 'snapshot_get', return_snapshot)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
|
||||
self.stubs.Set(self.volume_api, 'update_snapshot_metadata',
|
||||
fake_update_snapshot_metadata)
|
||||
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
self.snapshot_controller = snapshots.SnapshotsController(self.ext_mgr)
|
||||
self.controller = snapshot_metadata.Controller()
|
||||
self.id = str(uuid.uuid4())
|
||||
self.url = '/v2/fake/snapshots/%s/metadata' % self.id
|
||||
|
||||
snap = {"volume_size": 100,
|
||||
"volume_id": "fake-vol-id",
|
||||
"display_name": "Volume Test Name",
|
||||
"display_description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1",
|
||||
"host": "fake-host",
|
||||
"metadata": {}}
|
||||
body = {"snapshot": snap}
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
self.snapshot_controller.create(req, body)
|
||||
|
||||
def test_index(self):
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
'key3': 'value3',
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_index_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.index, req, self.url)
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
res_dict = self.controller.index(req, self.id)
|
||||
expected = {'metadata': {}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show(self):
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
res_dict = self.controller.show(req, self.id, 'key2')
|
||||
expected = {'meta': {'key2': 'value2'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_show_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key2')
|
||||
|
||||
def test_show_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, self.id, 'key6')
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_delete',
|
||||
delete_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key2')
|
||||
req.method = 'DELETE'
|
||||
res = self.controller.delete(req, self.id, 'key2')
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_delete_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key1')
|
||||
|
||||
def test_delete_meta_not_found(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key6')
|
||||
req.method = 'DELETE'
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, req, self.id, 'key6')
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_empty_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshot_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
res_dict = self.controller.create(req, self.id, body)
|
||||
self.assertEqual(body, res_dict)
|
||||
|
||||
def test_create_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, None)
|
||||
|
||||
def test_create_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_create_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req, self.id, body)
|
||||
|
||||
def test_create_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_get',
|
||||
return_snapshot_metadata)
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshot_metadata')
|
||||
req.method = 'POST'
|
||||
req.content_type = "application/json"
|
||||
body = {"metadata": {"key9": "value9"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.create, req, self.id, body)
|
||||
|
||||
def test_update_all(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {
|
||||
'metadata': {
|
||||
'key10': 'value10',
|
||||
'key99': 'value99',
|
||||
},
|
||||
}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_empty_container(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
res_dict = self.controller.update_all(req, self.id, expected)
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_all_malformed_container(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'meta': {}}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_malformed_data(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
expected = {'metadata': ['asdf']}
|
||||
req.body = jsonutils.dumps(expected)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update_all, req, self.id, expected)
|
||||
|
||||
def test_update_all_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get', return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'PUT'
|
||||
req.content_type = "application/json"
|
||||
body = {'metadata': {'key10': 'value10'}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update_all, req, '100', body)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res_dict = self.controller.update(req, self.id, 'key1', body)
|
||||
expected = {'meta': {'key1': 'value1'}}
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_update_item_nonexistent_snapshot(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_get',
|
||||
return_snapshot_nonexistent)
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/v2/fake/snapshots/asdf/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_empty_body(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', None)
|
||||
|
||||
def test_update_item_empty_key(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, '', body)
|
||||
|
||||
def test_update_item_key_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {("a" * 260): "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, ("a" * 260), body)
|
||||
|
||||
def test_update_item_value_too_long(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": ("a" * 260)}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.update,
|
||||
req, self.id, "key1", body)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/key1')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1", "key2": "value2"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'key1', body)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url + '/bad')
|
||||
req.method = 'PUT'
|
||||
body = {"meta": {"key1": "value1"}}
|
||||
req.body = jsonutils.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, self.id, 'bad', body)
|
||||
|
||||
def test_invalid_metadata_items_on_create(self):
|
||||
self.stubs.Set(manila.db, 'snapshot_metadata_update',
|
||||
return_create_snapshot_metadata)
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
#test for long key
|
||||
data = {"metadata": {"a" * 260: "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for long value
|
||||
data = {"metadata": {"key": "v" * 260}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
|
||||
self.controller.create, req, self.id, data)
|
||||
|
||||
#test for empty key.
|
||||
data = {"metadata": {"": "value1"}}
|
||||
req.body = jsonutils.dumps(data)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, self.id, data)
|
|
@ -1,425 +0,0 @@
|
|||
# Copyright 2011 Denali Systems, Inc.
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.v2 import snapshots
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests.api.v2 import stubs
|
||||
from manila import volume
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
UUID = '00000000-0000-0000-0000-000000000001'
|
||||
INVALID_UUID = '00000000-0000-0000-0000-000000000002'
|
||||
|
||||
|
||||
def _get_default_snapshot_param():
|
||||
return {
|
||||
'id': UUID,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'volume_size': 100,
|
||||
'created_at': None,
|
||||
'display_name': 'Default name',
|
||||
'display_description': 'Default description',
|
||||
}
|
||||
|
||||
|
||||
def stub_snapshot_create(self, context,
|
||||
volume_id, name,
|
||||
description, metadata):
|
||||
snapshot = _get_default_snapshot_param()
|
||||
snapshot['volume_id'] = volume_id
|
||||
snapshot['display_name'] = name
|
||||
snapshot['display_description'] = description
|
||||
snapshot['metadata'] = metadata
|
||||
return snapshot
|
||||
|
||||
|
||||
def stub_snapshot_delete(self, context, snapshot):
|
||||
if snapshot['id'] != UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
|
||||
def stub_snapshot_get(self, context, snapshot_id):
|
||||
if snapshot_id != UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
param = _get_default_snapshot_param()
|
||||
return param
|
||||
|
||||
|
||||
def stub_snapshot_get_all(self, context, search_opts=None):
|
||||
param = _get_default_snapshot_param()
|
||||
return [param]
|
||||
|
||||
|
||||
class SnapshotApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SnapshotApiTest, self).setUp()
|
||||
self.controller = snapshots.SnapshotsController()
|
||||
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stubs.stub_snapshot_get_all_by_project)
|
||||
self.stubs.Set(db, 'snapshot_get_all',
|
||||
stubs.stub_snapshot_get_all)
|
||||
|
||||
def test_snapshot_create(self):
|
||||
self.stubs.Set(volume.api.API, "create_snapshot", stub_snapshot_create)
|
||||
self.stubs.Set(volume.api.API, 'get', stubs.stub_volume_get)
|
||||
snapshot_name = 'Snapshot Test Name'
|
||||
snapshot_description = 'Snapshot Test Desc'
|
||||
snapshot = {
|
||||
"volume_id": '12',
|
||||
"force": False,
|
||||
"name": snapshot_name,
|
||||
"description": snapshot_description
|
||||
}
|
||||
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
resp_dict = self.controller.create(req, body)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['name'],
|
||||
snapshot_name)
|
||||
self.assertEqual(resp_dict['snapshot']['description'],
|
||||
snapshot_description)
|
||||
|
||||
def test_snapshot_create_force(self):
|
||||
self.stubs.Set(volume.api.API, "create_snapshot_force",
|
||||
stub_snapshot_create)
|
||||
self.stubs.Set(volume.api.API, 'get', stubs.stub_volume_get)
|
||||
snapshot_name = 'Snapshot Test Name'
|
||||
snapshot_description = 'Snapshot Test Desc'
|
||||
snapshot = {
|
||||
"volume_id": '12',
|
||||
"force": True,
|
||||
"name": snapshot_name,
|
||||
"description": snapshot_description
|
||||
}
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
resp_dict = self.controller.create(req, body)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['name'],
|
||||
snapshot_name)
|
||||
self.assertEqual(resp_dict['snapshot']['description'],
|
||||
snapshot_description)
|
||||
|
||||
snapshot = {
|
||||
"volume_id": "12",
|
||||
"force": "**&&^^%%$$##@@",
|
||||
"name": "Snapshot Test Name",
|
||||
"description": "Snapshot Test Desc"
|
||||
}
|
||||
body = dict(snapshot=snapshot)
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_snapshot_update(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
self.stubs.Set(volume.api.API, "update_snapshot",
|
||||
stubs.stub_snapshot_update)
|
||||
updates = {
|
||||
"name": "Updated Test Name",
|
||||
}
|
||||
body = {"snapshot": updates}
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % UUID)
|
||||
res_dict = self.controller.update(req, UUID, body)
|
||||
expected = {
|
||||
'snapshot': {
|
||||
'id': UUID,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'size': 100,
|
||||
'created_at': None,
|
||||
'name': 'Updated Test Name',
|
||||
'description': 'Default description',
|
||||
'metadata': {},
|
||||
}
|
||||
}
|
||||
self.assertEquals(expected, res_dict)
|
||||
|
||||
def test_snapshot_update_missing_body(self):
|
||||
body = {}
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % UUID)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, UUID, body)
|
||||
|
||||
def test_snapshot_update_invalid_body(self):
|
||||
body = {'name': 'missing top level snapshot key'}
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % UUID)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, UUID, body)
|
||||
|
||||
def test_snapshot_update_not_found(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
updates = {
|
||||
"name": "Updated Test Name",
|
||||
}
|
||||
body = {"snapshot": updates}
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/not-the-uuid')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.update, req,
|
||||
'not-the-uuid', body)
|
||||
|
||||
def test_snapshot_delete(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
self.stubs.Set(volume.api.API, "delete_snapshot", stub_snapshot_delete)
|
||||
|
||||
snapshot_id = UUID
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % snapshot_id)
|
||||
resp = self.controller.delete(req, snapshot_id)
|
||||
self.assertEqual(resp.status_int, 202)
|
||||
|
||||
def test_snapshot_delete_invalid_id(self):
|
||||
self.stubs.Set(volume.api.API, "delete_snapshot", stub_snapshot_delete)
|
||||
snapshot_id = INVALID_UUID
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % snapshot_id)
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
|
||||
req, snapshot_id)
|
||||
|
||||
def test_snapshot_show(self):
|
||||
self.stubs.Set(volume.api.API, "get_snapshot", stub_snapshot_get)
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % UUID)
|
||||
resp_dict = self.controller.show(req, UUID)
|
||||
|
||||
self.assertTrue('snapshot' in resp_dict)
|
||||
self.assertEqual(resp_dict['snapshot']['id'], UUID)
|
||||
|
||||
def test_snapshot_show_invalid_id(self):
|
||||
snapshot_id = INVALID_UUID
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/%s' % snapshot_id)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, snapshot_id)
|
||||
|
||||
def test_snapshot_detail(self):
|
||||
self.stubs.Set(volume.api.API, "get_all_snapshots",
|
||||
stub_snapshot_get_all)
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots/detail')
|
||||
resp_dict = self.controller.detail(req)
|
||||
|
||||
self.assertTrue('snapshots' in resp_dict)
|
||||
resp_snapshots = resp_dict['snapshots']
|
||||
self.assertEqual(len(resp_snapshots), 1)
|
||||
|
||||
resp_snapshot = resp_snapshots.pop()
|
||||
self.assertEqual(resp_snapshot['id'], UUID)
|
||||
|
||||
def test_snapshot_list_by_status(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, display_name='backup1',
|
||||
status='available'),
|
||||
stubs.stub_snapshot(2, display_name='backup2',
|
||||
status='available'),
|
||||
stubs.stub_snapshot(3, display_name='backup3',
|
||||
status='creating'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# no status filter
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 3)
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?status=creating')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['status'], 'creating')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?status=available')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 2)
|
||||
for snapshot in resp['snapshots']:
|
||||
self.assertEquals(snapshot['status'], 'available')
|
||||
# no match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?status=error')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 0)
|
||||
|
||||
def test_snapshot_list_by_volume(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, volume_id='vol1', status='creating'),
|
||||
stubs.stub_snapshot(2, volume_id='vol1', status='available'),
|
||||
stubs.stub_snapshot(3, volume_id='vol2', status='available'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?volume_id=vol2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['volume_id'], 'vol2')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?volume_id=vol1')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 2)
|
||||
for snapshot in resp['snapshots']:
|
||||
self.assertEqual(snapshot['volume_id'], 'vol1')
|
||||
# multiple filters
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?volume_id=vol1'
|
||||
'&status=available')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEqual(resp['snapshots'][0]['volume_id'], 'vol1')
|
||||
self.assertEqual(resp['snapshots'][0]['status'], 'available')
|
||||
|
||||
def test_snapshot_list_by_name(self):
|
||||
def stub_snapshot_get_all_by_project(context, project_id):
|
||||
return [
|
||||
stubs.stub_snapshot(1, display_name='backup1'),
|
||||
stubs.stub_snapshot(2, display_name='backup2'),
|
||||
stubs.stub_snapshot(3, display_name='backup3'),
|
||||
]
|
||||
self.stubs.Set(db, 'snapshot_get_all_by_project',
|
||||
stub_snapshot_get_all_by_project)
|
||||
|
||||
# no name filter
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 3)
|
||||
# filter by one name
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?name=backup2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 1)
|
||||
self.assertEquals(resp['snapshots'][0]['name'], 'backup2')
|
||||
# filter no match
|
||||
req = fakes.HTTPRequest.blank('/v2/snapshots?name=backup4')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['snapshots']), 0)
|
||||
|
||||
def test_admin_list_snapshots_limited_to_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
def test_admin_list_snapshots_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots?all_tenants=1',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(3, len(res['snapshots']))
|
||||
|
||||
def test_all_tenants_non_admin_gets_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots?all_tenants=1')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
def test_non_admin_get_by_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('snapshots' in res)
|
||||
self.assertEqual(1, len(res['snapshots']))
|
||||
|
||||
def _create_snapshot_bad_body(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/snapshots')
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._create_snapshot_bad_body(body=None)
|
||||
|
||||
def test_create_missing_snapshot(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._create_snapshot_bad_body(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'snapshot': 'string'}
|
||||
self._create_snapshot_bad_body(body=body)
|
||||
|
||||
|
||||
class SnapshotSerializerTest(test.TestCase):
|
||||
def _verify_snapshot(self, snap, tree):
|
||||
self.assertEqual(tree.tag, 'snapshot')
|
||||
|
||||
for attr in ('id', 'status', 'size', 'created_at',
|
||||
'name', 'description', 'volume_id'):
|
||||
self.assertEqual(str(snap[attr]), tree.get(attr))
|
||||
|
||||
def test_snapshot_show_create_serializer(self):
|
||||
serializer = snapshots.SnapshotTemplate()
|
||||
raw_snapshot = dict(
|
||||
id='snap_id',
|
||||
status='snap_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
name='snap_name',
|
||||
description='snap_desc',
|
||||
display_description='snap_desc',
|
||||
volume_id='vol_id',
|
||||
)
|
||||
text = serializer.serialize(dict(snapshot=raw_snapshot))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_snapshot(raw_snapshot, tree)
|
||||
|
||||
def test_snapshot_index_detail_serializer(self):
|
||||
serializer = snapshots.SnapshotsTemplate()
|
||||
raw_snapshots = [
|
||||
dict(
|
||||
id='snap1_id',
|
||||
status='snap1_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
name='snap1_name',
|
||||
description='snap1_desc',
|
||||
volume_id='vol1_id',
|
||||
),
|
||||
dict(
|
||||
id='snap2_id',
|
||||
status='snap2_status',
|
||||
size=1024,
|
||||
created_at=datetime.datetime.now(),
|
||||
name='snap2_name',
|
||||
description='snap2_desc',
|
||||
volume_id='vol2_id',
|
||||
)
|
||||
]
|
||||
text = serializer.serialize(dict(snapshots=raw_snapshots))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('snapshots', tree.tag)
|
||||
self.assertEqual(len(raw_snapshots), len(tree))
|
||||
for idx, child in enumerate(tree):
|
||||
self._verify_snapshot(raw_snapshots[idx], child)
|
|
@ -1,211 +0,0 @@
|
|||
# Copyright 2011 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 lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api.v2 import types
|
||||
from manila.api.views import types as views_types
|
||||
from manila import exception
|
||||
from manila.openstack.common import timeutils
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.volume import volume_types
|
||||
|
||||
|
||||
def stub_volume_type(id):
|
||||
specs = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"
|
||||
}
|
||||
return dict(
|
||||
id=id,
|
||||
name='vol_type_%s' % str(id),
|
||||
extra_specs=specs,
|
||||
)
|
||||
|
||||
|
||||
def return_volume_types_get_all_types(context):
|
||||
return dict(
|
||||
vol_type_1=stub_volume_type(1),
|
||||
vol_type_2=stub_volume_type(2),
|
||||
vol_type_3=stub_volume_type(3)
|
||||
)
|
||||
|
||||
|
||||
def return_empty_volume_types_get_all_types(context):
|
||||
return {}
|
||||
|
||||
|
||||
def return_volume_types_get_volume_type(context, id):
|
||||
if id == "777":
|
||||
raise exception.VolumeTypeNotFound(volume_type_id=id)
|
||||
return stub_volume_type(int(id))
|
||||
|
||||
|
||||
def return_volume_types_get_by_name(context, name):
|
||||
if name == "777":
|
||||
raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
|
||||
return stub_volume_type(int(name.split("_")[2]))
|
||||
|
||||
|
||||
class VolumeTypesApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeTypesApiTest, self).setUp()
|
||||
self.controller = types.VolumeTypesController()
|
||||
|
||||
def test_volume_types_index(self):
|
||||
self.stubs.Set(volume_types, 'get_all_types',
|
||||
return_volume_types_get_all_types)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(3, len(res_dict['volume_types']))
|
||||
|
||||
expected_names = ['vol_type_1', 'vol_type_2', 'vol_type_3']
|
||||
actual_names = map(lambda e: e['name'], res_dict['volume_types'])
|
||||
self.assertEqual(set(actual_names), set(expected_names))
|
||||
for entry in res_dict['volume_types']:
|
||||
self.assertEqual('value1', entry['extra_specs']['key1'])
|
||||
|
||||
def test_volume_types_index_no_data(self):
|
||||
self.stubs.Set(volume_types, 'get_all_types',
|
||||
return_empty_volume_types_get_all_types)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(0, len(res_dict['volume_types']))
|
||||
|
||||
def test_volume_types_show(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
res_dict = self.controller.show(req, 1)
|
||||
|
||||
self.assertEqual(1, len(res_dict))
|
||||
self.assertEqual('1', res_dict['volume_type']['id'])
|
||||
self.assertEqual('vol_type_1', res_dict['volume_type']['name'])
|
||||
|
||||
def test_volume_types_show_not_found(self):
|
||||
self.stubs.Set(volume_types, 'get_volume_type',
|
||||
return_volume_types_get_volume_type)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, '777')
|
||||
|
||||
def test_view_builder_show(self):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
|
||||
now = timeutils.isotime()
|
||||
raw_volume_type = dict(
|
||||
name='new_type',
|
||||
deleted=False,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
extra_specs={},
|
||||
deleted_at=None,
|
||||
id=42,
|
||||
)
|
||||
|
||||
request = fakes.HTTPRequest.blank("/v2")
|
||||
output = view_builder.show(request, raw_volume_type)
|
||||
|
||||
self.assertTrue('volume_type' in output)
|
||||
expected_volume_type = dict(
|
||||
name='new_type',
|
||||
extra_specs={},
|
||||
id=42,
|
||||
)
|
||||
self.assertDictMatch(output['volume_type'], expected_volume_type)
|
||||
|
||||
def test_view_builder_list(self):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
|
||||
now = timeutils.isotime()
|
||||
raw_volume_types = []
|
||||
for i in range(0, 10):
|
||||
raw_volume_types.append(
|
||||
dict(
|
||||
name='new_type',
|
||||
deleted=False,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
extra_specs={},
|
||||
deleted_at=None,
|
||||
id=42 + i
|
||||
)
|
||||
)
|
||||
|
||||
request = fakes.HTTPRequest.blank("/v2")
|
||||
output = view_builder.index(request, raw_volume_types)
|
||||
|
||||
self.assertTrue('volume_types' in output)
|
||||
for i in range(0, 10):
|
||||
expected_volume_type = dict(
|
||||
name='new_type',
|
||||
extra_specs={},
|
||||
id=42 + i
|
||||
)
|
||||
self.assertDictMatch(output['volume_types'][i],
|
||||
expected_volume_type)
|
||||
|
||||
|
||||
class VolumeTypesSerializerTest(test.TestCase):
|
||||
def _verify_volume_type(self, vtype, tree):
|
||||
self.assertEqual('volume_type', tree.tag)
|
||||
self.assertEqual(vtype['name'], tree.get('name'))
|
||||
self.assertEqual(str(vtype['id']), tree.get('id'))
|
||||
self.assertEqual(1, len(tree))
|
||||
extra_specs = tree[0]
|
||||
self.assertEqual('extra_specs', extra_specs.tag)
|
||||
seen = set(vtype['extra_specs'].keys())
|
||||
for child in extra_specs:
|
||||
self.assertTrue(child.tag in seen)
|
||||
self.assertEqual(vtype['extra_specs'][child.tag], child.text)
|
||||
seen.remove(child.tag)
|
||||
self.assertEqual(len(seen), 0)
|
||||
|
||||
def test_index_serializer(self):
|
||||
serializer = types.VolumeTypesTemplate()
|
||||
|
||||
# Just getting some input data
|
||||
vtypes = return_volume_types_get_all_types(None)
|
||||
text = serializer.serialize({'volume_types': vtypes.values()})
|
||||
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('volume_types', tree.tag)
|
||||
self.assertEqual(len(vtypes), len(tree))
|
||||
for child in tree:
|
||||
name = child.get('name')
|
||||
self.assertTrue(name in vtypes)
|
||||
self._verify_volume_type(vtypes[name], child)
|
||||
|
||||
def test_voltype_serializer(self):
|
||||
serializer = types.VolumeTypeTemplate()
|
||||
|
||||
vtype = stub_volume_type(1)
|
||||
text = serializer.serialize(dict(volume_type=vtype))
|
||||
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_volume_type(vtype, tree)
|
|
@ -1,955 +0,0 @@
|
|||
# Copyright 2013 Josh Durgin
|
||||
# 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.
|
||||
|
||||
import datetime
|
||||
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v2 import volumes
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests.api.v2 import stubs
|
||||
from manila.tests.image import fake as fake_image
|
||||
from manila.volume import api as volume_api
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
NS = '{http://docs.openstack.org/api/openstack-volume/2.0/content}'
|
||||
|
||||
TEST_SNAPSHOT_UUID = '00000000-0000-0000-0000-000000000001'
|
||||
|
||||
|
||||
def stub_snapshot_get(self, context, snapshot_id):
|
||||
if snapshot_id != TEST_SNAPSHOT_UUID:
|
||||
raise exception.NotFound
|
||||
|
||||
return {
|
||||
'id': snapshot_id,
|
||||
'volume_id': 12,
|
||||
'status': 'available',
|
||||
'volume_size': 100,
|
||||
'created_at': None,
|
||||
'name': 'Default name',
|
||||
'description': 'Default description',
|
||||
}
|
||||
|
||||
|
||||
class VolumeApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeApiTest, self).setUp()
|
||||
self.ext_mgr = extensions.ExtensionManager()
|
||||
self.ext_mgr.extensions = {}
|
||||
fake_image.stub_out_image_service(self.stubs)
|
||||
self.controller = volumes.VolumeController(self.ext_mgr)
|
||||
|
||||
self.stubs.Set(db, 'volume_get_all', stubs.stub_volume_get_all)
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
||||
self.stubs.Set(volume_api.API, 'delete', stubs.stub_volume_delete)
|
||||
self.maxDiff = None
|
||||
|
||||
def test_volume_create(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
|
||||
vol = {
|
||||
"size": 100,
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1"
|
||||
}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
expected = {
|
||||
'volume': {
|
||||
'name': 'Volume Test Name',
|
||||
'id': '1',
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_create_with_type(self):
|
||||
vol_type = db.volume_type_create(context.get_admin_context(),
|
||||
dict(name=FLAGS.default_volume_type,
|
||||
extra_specs={}))
|
||||
|
||||
db_vol_type = db.volume_type_get(context.get_admin_context(),
|
||||
vol_type.id)
|
||||
|
||||
vol = {
|
||||
"size": 100,
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1",
|
||||
"volume_type": db_vol_type['id'],
|
||||
}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
volume_id = res_dict['volume']['id']
|
||||
self.assertEquals(len(res_dict), 1)
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get_all',
|
||||
lambda *args, **kwargs:
|
||||
[stubs.stub_volume(volume_id,
|
||||
volume_type={'name': vol_type})])
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail')
|
||||
res_dict = self.controller.detail(req)
|
||||
|
||||
def test_volume_creation_fails_with_bad_size(self):
|
||||
vol = {"size": '',
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "zone1:host1"}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_create_with_image_id(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
vol = {"size": '1',
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "nova",
|
||||
"imageRef": 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'}
|
||||
expected = {
|
||||
'volume': {
|
||||
'name': 'Volume Test Name',
|
||||
'id': '1',
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
res_dict = self.controller.create(req, body)
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_create_with_image_id_is_integer(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
vol = {
|
||||
"size": '1',
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "manila",
|
||||
"imageRef": 1234,
|
||||
}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_create_with_image_id_not_uuid_format(self):
|
||||
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
|
||||
self.ext_mgr.extensions = {'os-image-create': 'fake'}
|
||||
vol = {
|
||||
"size": '1',
|
||||
"name": "Volume Test Name",
|
||||
"description": "Volume Test Desc",
|
||||
"availability_zone": "manila",
|
||||
"imageRef": '12345'
|
||||
}
|
||||
body = {"volume": vol}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create,
|
||||
req,
|
||||
body)
|
||||
|
||||
def test_volume_update(self):
|
||||
self.stubs.Set(volume_api.API, "update", stubs.stub_volume_update)
|
||||
updates = {
|
||||
"name": "Updated Test Name",
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
res_dict = self.controller.update(req, '1', body)
|
||||
expected = {
|
||||
'volume': {
|
||||
'status': 'fakestatus',
|
||||
'description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'name': 'Updated Test Name',
|
||||
'attachments': [
|
||||
{
|
||||
'id': '1',
|
||||
'volume_id': '1',
|
||||
'server_id': 'fakeuuid',
|
||||
'device': '/',
|
||||
}
|
||||
],
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
self.assertEquals(res_dict, expected)
|
||||
|
||||
def test_volume_update_metadata(self):
|
||||
self.stubs.Set(volume_api.API, "update", stubs.stub_volume_update)
|
||||
updates = {
|
||||
"metadata": {"qos_max_iops": 2000}
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
res_dict = self.controller.update(req, '1', body)
|
||||
expected = {'volume': {
|
||||
'status': 'fakestatus',
|
||||
'description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'name': 'displayname',
|
||||
'attachments': [{
|
||||
'id': '1',
|
||||
'volume_id': '1',
|
||||
'server_id': 'fakeuuid',
|
||||
'device': '/',
|
||||
}],
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {"qos_max_iops": 2000},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}}
|
||||
self.assertEquals(res_dict, expected)
|
||||
|
||||
def test_update_empty_body(self):
|
||||
body = {}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_update_invalid_body(self):
|
||||
body = {
|
||||
'name': 'missing top level volume key'
|
||||
}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_update_not_found(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
updates = {
|
||||
"name": "Updated Test Name",
|
||||
}
|
||||
body = {"volume": updates}
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update,
|
||||
req, '1', body)
|
||||
|
||||
def test_volume_list_summary(self):
|
||||
self.stubs.Set(volume_api.API, 'get_all',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
res_dict = self.controller.index(req)
|
||||
expected = {
|
||||
'volumes': [
|
||||
{
|
||||
'name': 'displayname',
|
||||
'id': '1',
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_list_detail(self):
|
||||
self.stubs.Set(volume_api.API, 'get_all',
|
||||
stubs.stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail')
|
||||
res_dict = self.controller.detail(req)
|
||||
expected = {
|
||||
'volumes': [
|
||||
{
|
||||
'status': 'fakestatus',
|
||||
'description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'name': 'displayname',
|
||||
'attachments': [
|
||||
{
|
||||
'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'
|
||||
}
|
||||
],
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_index_with_marker(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?marker=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 2)
|
||||
self.assertEquals(volumes[0]['id'], 1)
|
||||
self.assertEquals(volumes[1]['id'], 2)
|
||||
|
||||
def test_volume_index_limit(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
|
||||
def test_volume_index_limit_negative(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1')
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_index_limit_non_int(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a')
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_index_limit_marker(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?marker=1&limit=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
self.assertEquals(volumes[0]['id'], '1')
|
||||
|
||||
def test_volume_index_limit_offset(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=2&offset=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
self.assertEquals(volumes[0]['id'], 2)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1&offset=1')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a&offset=1')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_detail_with_marker(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?marker=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 2)
|
||||
self.assertEquals(volumes[0]['id'], 1)
|
||||
self.assertEquals(volumes[1]['id'], 2)
|
||||
|
||||
def test_volume_detail_limit(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
|
||||
def test_volume_detail_limit_negative(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1')
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_detail_limit_non_int(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a')
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_detail_limit_marker(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?marker=1&limit=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
self.assertEquals(volumes[0]['id'], '1')
|
||||
|
||||
def test_volume_detail_limit_offset(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=2&offset=1')
|
||||
res_dict = self.controller.index(req)
|
||||
volumes = res_dict['volumes']
|
||||
self.assertEquals(len(volumes), 1)
|
||||
self.assertEquals(volumes[0]['id'], 2)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1&offset=1')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a&offset=1')
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.controller.index,
|
||||
req)
|
||||
|
||||
def test_volume_list_by_name(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1'),
|
||||
stubs.stub_volume(2, display_name='vol2'),
|
||||
stubs.stub_volume(3, display_name='vol3'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
|
||||
# no name filter
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 3)
|
||||
# filter on name
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?name=vol2')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['name'], 'vol2')
|
||||
# filter no match
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes?name=vol4')
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual(len(resp['volumes']), 0)
|
||||
|
||||
def test_volume_list_by_status(self):
|
||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_key, sort_dir):
|
||||
return [
|
||||
stubs.stub_volume(1, display_name='vol1', status='available'),
|
||||
stubs.stub_volume(2, display_name='vol2', status='available'),
|
||||
stubs.stub_volume(3, display_name='vol3', status='in-use'),
|
||||
]
|
||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||
stub_volume_get_all_by_project)
|
||||
# no status filter
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/details')
|
||||
resp = self.controller.detail(req)
|
||||
self.assertEqual(len(resp['volumes']), 3)
|
||||
# single match
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/details?status=in-use')
|
||||
resp = self.controller.detail(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['status'], 'in-use')
|
||||
# multiple match
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/details/?status=available')
|
||||
resp = self.controller.detail(req)
|
||||
self.assertEqual(len(resp['volumes']), 2)
|
||||
for volume in resp['volumes']:
|
||||
self.assertEqual(volume['status'], 'available')
|
||||
# multiple filters
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/details/?status=available&'
|
||||
'name=vol1')
|
||||
resp = self.controller.detail(req)
|
||||
self.assertEqual(len(resp['volumes']), 1)
|
||||
self.assertEqual(resp['volumes'][0]['name'], 'vol1')
|
||||
self.assertEqual(resp['volumes'][0]['status'], 'available')
|
||||
# no match
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/details?status=in-use&'
|
||||
'name=vol1')
|
||||
resp = self.controller.detail(req)
|
||||
self.assertEqual(len(resp['volumes']), 0)
|
||||
|
||||
def test_volume_show(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = {
|
||||
'volume': {
|
||||
'status': 'fakestatus',
|
||||
'description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'name': 'displayname',
|
||||
'attachments': [
|
||||
{
|
||||
'device': '/',
|
||||
'server_id': 'fakeuuid',
|
||||
'id': '1',
|
||||
'volume_id': '1'
|
||||
}
|
||||
],
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_show_no_attachments(self):
|
||||
def stub_volume_get(self, context, volume_id):
|
||||
return stubs.stub_volume(volume_id, attach_status='detached')
|
||||
|
||||
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = {
|
||||
'volume': {
|
||||
'status': 'fakestatus',
|
||||
'description': 'displaydesc',
|
||||
'availability_zone': 'fakeaz',
|
||||
'name': 'displayname',
|
||||
'attachments': [],
|
||||
'volume_type': 'vol_type_name',
|
||||
'snapshot_id': None,
|
||||
'source_volid': None,
|
||||
'metadata': {},
|
||||
'id': '1',
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'size': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': 'http://localhost/v1/fake/volumes/1',
|
||||
'rel': 'self'
|
||||
},
|
||||
{
|
||||
'href': 'http://localhost/fake/volumes/1',
|
||||
'rel': 'bookmark'
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
self.assertEqual(res_dict, expected)
|
||||
|
||||
def test_volume_show_no_volume(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, 1)
|
||||
|
||||
def test_volume_delete(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
resp = self.controller.delete(req, 1)
|
||||
self.assertEqual(resp.status_int, 202)
|
||||
|
||||
def test_volume_delete_no_volume(self):
|
||||
self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound)
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/volumes/1')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
|
||||
req, 1)
|
||||
|
||||
def test_admin_list_volumes_limited_to_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
def test_admin_list_volumes_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes?all_tenants=1',
|
||||
use_admin_context=True)
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(3, len(res['volumes']))
|
||||
|
||||
def test_all_tenants_non_admin_gets_all_tenants(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes?all_tenants=1')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
def test_non_admin_get_by_project(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes')
|
||||
res = self.controller.index(req)
|
||||
self.assertTrue('volumes' in res)
|
||||
self.assertEqual(1, len(res['volumes']))
|
||||
|
||||
def _create_volume_bad_request(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/volumes')
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, body)
|
||||
|
||||
def test_create_no_body(self):
|
||||
self._create_volume_bad_request(body=None)
|
||||
|
||||
def test_create_missing_volume(self):
|
||||
body = {'foo': {'a': 'b'}}
|
||||
self._create_volume_bad_request(body=body)
|
||||
|
||||
def test_create_malformed_entity(self):
|
||||
body = {'volume': 'string'}
|
||||
self._create_volume_bad_request(body=body)
|
||||
|
||||
|
||||
class VolumeSerializerTest(test.TestCase):
|
||||
def _verify_volume_attachment(self, attach, tree):
|
||||
for attr in ('id', 'volume_id', 'server_id', 'device'):
|
||||
self.assertEqual(str(attach[attr]), tree.get(attr))
|
||||
|
||||
def _verify_volume(self, vol, tree):
|
||||
self.assertEqual(tree.tag, NS + 'volume')
|
||||
|
||||
for attr in ('id', 'status', 'size', 'availability_zone', 'created_at',
|
||||
'name', 'description', 'volume_type',
|
||||
'snapshot_id', 'source_volid'):
|
||||
self.assertEqual(str(vol[attr]), tree.get(attr))
|
||||
|
||||
for child in tree:
|
||||
print child.tag
|
||||
self.assertTrue(child.tag in (NS + 'attachments', NS + 'metadata'))
|
||||
if child.tag == 'attachments':
|
||||
self.assertEqual(1, len(child))
|
||||
self.assertEqual('attachment', child[0].tag)
|
||||
self._verify_volume_attachment(vol['attachments'][0], child[0])
|
||||
elif child.tag == 'metadata':
|
||||
not_seen = set(vol['metadata'].keys())
|
||||
for gr_child in child:
|
||||
self.assertTrue(gr_child.get("key") in not_seen)
|
||||
self.assertEqual(str(vol['metadata'][gr_child.get("key")]),
|
||||
gr_child.text)
|
||||
not_seen.remove(gr_child.get('key'))
|
||||
self.assertEqual(0, len(not_seen))
|
||||
|
||||
def test_volume_show_create_serializer(self):
|
||||
serializer = volumes.VolumeTemplate()
|
||||
raw_volume = dict(
|
||||
id='vol_id',
|
||||
status='vol_status',
|
||||
size=1024,
|
||||
availability_zone='vol_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[
|
||||
dict(
|
||||
id='vol_id',
|
||||
volume_id='vol_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo'
|
||||
)
|
||||
],
|
||||
name='vol_name',
|
||||
description='vol_desc',
|
||||
volume_type='vol_type',
|
||||
snapshot_id='snap_id',
|
||||
source_volid='source_volid',
|
||||
metadata=dict(
|
||||
foo='bar',
|
||||
baz='quux',
|
||||
),
|
||||
)
|
||||
text = serializer.serialize(dict(volume=raw_volume))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self._verify_volume(raw_volume, tree)
|
||||
|
||||
def test_volume_index_detail_serializer(self):
|
||||
serializer = volumes.VolumesTemplate()
|
||||
raw_volumes = [
|
||||
dict(
|
||||
id='vol1_id',
|
||||
status='vol1_status',
|
||||
size=1024,
|
||||
availability_zone='vol1_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[
|
||||
dict(
|
||||
id='vol1_id',
|
||||
volume_id='vol1_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo1'
|
||||
)
|
||||
],
|
||||
name='vol1_name',
|
||||
description='vol1_desc',
|
||||
volume_type='vol1_type',
|
||||
snapshot_id='snap1_id',
|
||||
source_volid=None,
|
||||
metadata=dict(foo='vol1_foo',
|
||||
bar='vol1_bar', ), ),
|
||||
dict(
|
||||
id='vol2_id',
|
||||
status='vol2_status',
|
||||
size=1024,
|
||||
availability_zone='vol2_availability',
|
||||
created_at=datetime.datetime.now(),
|
||||
attachments=[dict(id='vol2_id',
|
||||
volume_id='vol2_id',
|
||||
server_id='instance_uuid',
|
||||
device='/foo2')],
|
||||
name='vol2_name',
|
||||
description='vol2_desc',
|
||||
volume_type='vol2_type',
|
||||
snapshot_id='snap2_id',
|
||||
source_volid=None,
|
||||
metadata=dict(foo='vol2_foo',
|
||||
bar='vol2_bar', ), )]
|
||||
text = serializer.serialize(dict(volumes=raw_volumes))
|
||||
|
||||
print text
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual(NS + 'volumes', tree.tag)
|
||||
self.assertEqual(len(raw_volumes), len(tree))
|
||||
for idx, child in enumerate(tree):
|
||||
self._verify_volume(raw_volumes[idx], child)
|
||||
|
||||
|
||||
class TestVolumeCreateRequestXMLDeserializer(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVolumeCreateRequestXMLDeserializer, self).setUp()
|
||||
self.deserializer = volumes.CreateDeserializer()
|
||||
|
||||
def test_minimal_volume(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_name(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"
|
||||
name="Volume-xml"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"name": "Volume-xml",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_description(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"
|
||||
name="Volume-xml"
|
||||
description="description"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"name": "Volume-xml",
|
||||
"description": "description",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_volume_type(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"
|
||||
name="Volume-xml"
|
||||
description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"name": "Volume-xml",
|
||||
"size": "1",
|
||||
"name": "Volume-xml",
|
||||
"description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_availability_zone(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"
|
||||
name="Volume-xml"
|
||||
description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
|
||||
availability_zone="us-east1"></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"name": "Volume-xml",
|
||||
"description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
"availability_zone": "us-east1",
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_metadata(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
name="Volume-xml"
|
||||
size="1">
|
||||
<metadata><meta key="Type">work</meta></metadata></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"name": "Volume-xml",
|
||||
"size": "1",
|
||||
"metadata": {
|
||||
"Type": "work",
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
||||
|
||||
def test_full_volume(self):
|
||||
self_request = """
|
||||
<volume xmlns="http://docs.openstack.org/api/openstack-volume/2.0/content"
|
||||
size="1"
|
||||
name="Volume-xml"
|
||||
description="description"
|
||||
volume_type="289da7f8-6440-407c-9fb4-7db01ec49164"
|
||||
availability_zone="us-east1">
|
||||
<metadata><meta key="Type">work</meta></metadata></volume>"""
|
||||
request = self.deserializer.deserialize(self_request)
|
||||
expected = {
|
||||
"volume": {
|
||||
"size": "1",
|
||||
"name": "Volume-xml",
|
||||
"description": "description",
|
||||
"volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164",
|
||||
"availability_zone": "us-east1",
|
||||
"metadata": {
|
||||
"Type": "work",
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEquals(request['body'], expected)
|
|
@ -1,14 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
|
@ -1,41 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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 manila.db import base
|
||||
from manila.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeBackupService(base.Base):
|
||||
def __init__(self, context, db_driver=None):
|
||||
super(FakeBackupService, self).__init__(db_driver)
|
||||
|
||||
def backup(self, backup, volume_file):
|
||||
pass
|
||||
|
||||
def restore(self, backup, volume_id, volume_file):
|
||||
pass
|
||||
|
||||
def delete(self, backup):
|
||||
# if backup has magic name of 'fail_on_delete'
|
||||
# we raise an error - useful for some tests -
|
||||
# otherwise we return without error
|
||||
if backup['display_name'] == 'fail_on_delete':
|
||||
raise IOError('fake')
|
||||
|
||||
|
||||
def get_backup_service(context):
|
||||
return FakeBackupService(context)
|
|
@ -1,111 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
|
||||
import httplib
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import zlib
|
||||
|
||||
from manila.openstack.common import log as logging
|
||||
from swiftclient import client as swift
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeSwiftClient(object):
|
||||
"""Logs calls instead of executing."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def Connection(self, *args, **kargs):
|
||||
LOG.debug("fake FakeSwiftClient Connection")
|
||||
return FakeSwiftConnection()
|
||||
|
||||
|
||||
class FakeSwiftConnection(object):
|
||||
"""Logging calls instead of executing"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def head_container(self, container):
|
||||
LOG.debug("fake head_container(%s)" % container)
|
||||
if container == 'missing_container':
|
||||
raise swift.ClientException('fake exception',
|
||||
http_status=httplib.NOT_FOUND)
|
||||
elif container == 'unauthorized_container':
|
||||
raise swift.ClientException('fake exception',
|
||||
http_status=httplib.UNAUTHORIZED)
|
||||
elif container == 'socket_error_on_head':
|
||||
raise socket.error(111, 'ECONNREFUSED')
|
||||
pass
|
||||
|
||||
def put_container(self, container):
|
||||
LOG.debug("fake put_container(%s)" % container)
|
||||
pass
|
||||
|
||||
def get_container(self, container, **kwargs):
|
||||
LOG.debug("fake get_container(%s)" % container)
|
||||
fake_header = None
|
||||
fake_body = [{'name': 'backup_001'},
|
||||
{'name': 'backup_002'},
|
||||
{'name': 'backup_003'}]
|
||||
return fake_header, fake_body
|
||||
|
||||
def head_object(self, container, name):
|
||||
LOG.debug("fake put_container(%s, %s)" % (container, name))
|
||||
return {'etag': 'fake-md5-sum'}
|
||||
|
||||
def get_object(self, container, name):
|
||||
LOG.debug("fake get_object(%s, %s)" % (container, name))
|
||||
if container == 'socket_error_on_get':
|
||||
raise socket.error(111, 'ECONNREFUSED')
|
||||
if 'metadata' in name:
|
||||
fake_object_header = None
|
||||
metadata = {}
|
||||
if container == 'unsupported_version':
|
||||
metadata['version'] = '9.9.9'
|
||||
else:
|
||||
metadata['version'] = '1.0.0'
|
||||
metadata['backup_id'] = 123
|
||||
metadata['volume_id'] = 123
|
||||
metadata['backup_name'] = 'fake backup'
|
||||
metadata['backup_description'] = 'fake backup description'
|
||||
metadata['created_at'] = '2013-02-19 11:20:54,805'
|
||||
metadata['objects'] = [{
|
||||
'backup_001': {'compression': 'zlib', 'length': 10},
|
||||
'backup_002': {'compression': 'zlib', 'length': 10},
|
||||
'backup_003': {'compression': 'zlib', 'length': 10}
|
||||
}]
|
||||
metadata_json = json.dumps(metadata, sort_keys=True, indent=2)
|
||||
fake_object_body = metadata_json
|
||||
return (fake_object_header, fake_object_body)
|
||||
|
||||
fake_header = None
|
||||
fake_object_body = os.urandom(1024 * 1024)
|
||||
return (fake_header, zlib.compress(fake_object_body))
|
||||
|
||||
def put_object(self, container, name, reader):
|
||||
LOG.debug("fake put_object(%s, %s)" % (container, name))
|
||||
if container == 'socket_error_on_put':
|
||||
raise socket.error(111, 'ECONNREFUSED')
|
||||
return 'fake-md5-sum'
|
||||
|
||||
def delete_object(self, container, name):
|
||||
LOG.debug("fake delete_object(%s, %s)" % (container, name))
|
||||
if container == 'socket_error_on_delete':
|
||||
raise socket.error(111, 'ECONNREFUSED')
|
||||
pass
|
|
@ -1,16 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation.
|
||||
# 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.
|
|
@ -1,144 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import mox
|
||||
|
||||
|
||||
from manila.brick.local_dev import lvm as brick
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common import processutils
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_configuration():
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
return configuration
|
||||
|
||||
|
||||
class BrickLvmTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
self._mox = mox.Mox()
|
||||
self.configuration = mox.MockObject(conf.Configuration)
|
||||
self.configuration.volume_group_name = 'fake-volumes'
|
||||
super(BrickLvmTestCase, self).setUp()
|
||||
self.stubs.Set(processutils, 'execute',
|
||||
self.fake_execute)
|
||||
self.vg = brick.LVM(self.configuration.volume_group_name)
|
||||
|
||||
def failed_fake_execute(obj, *cmd, **kwargs):
|
||||
return ("\n", "fake-error")
|
||||
|
||||
def fake_pretend_lvm_version(obj, *cmd, **kwargs):
|
||||
return (" LVM version: 2.03.00 (2012-03-06)\n", "")
|
||||
|
||||
def fake_old_lvm_version(obj, *cmd, **kwargs):
|
||||
return (" LVM version: 2.02.65(2) (2012-03-06)\n", "")
|
||||
|
||||
def fake_execute(obj, *cmd, **kwargs):
|
||||
cmd_string = ', '.join(cmd)
|
||||
data = "\n"
|
||||
|
||||
if 'vgs, --noheadings, -o, name' == cmd_string:
|
||||
data = " fake-volumes\n"
|
||||
if 'vgs, --version' in cmd_string:
|
||||
data = " LVM version: 2.02.95(2) (2012-03-06)\n"
|
||||
elif 'vgs, --noheadings, -o uuid, fake-volumes' in cmd_string:
|
||||
data = " kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
|
||||
elif 'vgs, --noheadings, -o, name,size,free,lv_count,uuid' in\
|
||||
cmd_string:
|
||||
data = " fake-volumes:10.00g:10.00g:0:"\
|
||||
"kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
|
||||
if 'fake-volumes' in cmd_string:
|
||||
return (data, "")
|
||||
data += " fake-volumes-2:10.00g:10.00g:0:"\
|
||||
"lWyauW-dKpG-Rz7E-xtKY-jeju-QsYU-SLG7Z2\n"
|
||||
data += " fake-volumes-3:10.00g:10.00g:0:"\
|
||||
"mXzbuX-dKpG-Rz7E-xtKY-jeju-QsYU-SLG8Z3\n"
|
||||
elif 'lvs, --noheadings, -o, vg_name,name,size' in cmd_string:
|
||||
data = " fake-volumes fake-1 1.00g\n"
|
||||
data += " fake-volumes fake-2 1.00g\n"
|
||||
elif 'lvs, --noheadings, -o, vg_name,name,size' in cmd_string:
|
||||
data = " fake-volumes fake-1 1.00g\n"
|
||||
data += " fake-volumes fake-2 1.00g\n"
|
||||
elif 'pvs, --noheadings' and 'fake-volumes' in cmd_string:
|
||||
data = " fake-volumes:/dev/sda:10.00g:8.99g\n"
|
||||
elif 'pvs, --noheadings' in cmd_string:
|
||||
data = " fake-volumes:/dev/sda:10.00g:8.99g\n"
|
||||
data += " fake-volumes-2:/dev/sdb:10.00g:8.99g\n"
|
||||
data += " fake-volumes-3:/dev/sdc:10.00g:8.99g\n"
|
||||
else:
|
||||
pass
|
||||
|
||||
return (data, "")
|
||||
|
||||
def test_vg_exists(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertEqual(self.vg._vg_exists(), True)
|
||||
|
||||
self.stubs.Set(processutils, 'execute', self.failed_fake_execute)
|
||||
self.assertEqual(self.vg._vg_exists(), False)
|
||||
|
||||
def test_get_vg_uuid(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertEqual(self.vg._get_vg_uuid()[0],
|
||||
'kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1')
|
||||
|
||||
def test_get_all_volumes(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
out = self.vg.get_volumes()
|
||||
|
||||
self.assertEqual(out[0]['name'], 'fake-1')
|
||||
self.assertEqual(out[0]['size'], '1.00g')
|
||||
self.assertEqual(out[0]['vg'], 'fake-volumes')
|
||||
|
||||
def test_get_volume(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertEqual(self.vg.get_volume('fake-1')['name'], 'fake-1')
|
||||
|
||||
def test_get_all_physical_volumes(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
pvs = self.vg.get_all_physical_volumes()
|
||||
self.assertEqual(len(pvs), 3)
|
||||
|
||||
def test_get_physical_volumes(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
pvs = self.vg.get_physical_volumes()
|
||||
self.assertEqual(len(pvs), 1)
|
||||
|
||||
def test_get_volume_groups(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertEqual(len(self.vg.get_all_volume_groups()), 3)
|
||||
self.assertEqual(len(self.vg.get_all_volume_groups('fake-volumes')), 1)
|
||||
|
||||
def test_update_vg_info(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertEqual(self.vg.update_volume_group_info()['name'],
|
||||
'fake-volumes')
|
||||
|
||||
def test_thin_support(self):
|
||||
self.stubs.Set(processutils, 'execute', self.fake_execute)
|
||||
self.assertTrue(self.vg.supports_thin_provisioning())
|
||||
|
||||
self.stubs.Set(processutils, 'execute', self.fake_pretend_lvm_version)
|
||||
self.assertTrue(self.vg.supports_thin_provisioning())
|
||||
|
||||
self.stubs.Set(processutils, 'execute', self.fake_old_lvm_version)
|
||||
self.assertFalse(self.vg.supports_thin_provisioning())
|
|
@ -201,19 +201,3 @@ class TestOpenStackClient(object):
|
|||
kwargs['method'] = 'DELETE'
|
||||
kwargs.setdefault('check_response_status', [200, 202, 204])
|
||||
return self.api_request(relative_uri, **kwargs)
|
||||
|
||||
def get_volume(self, volume_id):
|
||||
return self.api_get('/volumes/%s' % volume_id)['volume']
|
||||
|
||||
def get_volumes(self, detail=True):
|
||||
rel_url = '/volumes/detail' if detail else '/volumes'
|
||||
return self.api_get(rel_url)['volumes']
|
||||
|
||||
def post_volume(self, volume):
|
||||
return self.api_post('/volumes', volume)['volume']
|
||||
|
||||
def delete_volume(self, volume_id):
|
||||
return self.api_delete('/volumes/%s' % volume_id)
|
||||
|
||||
def put_volume(self, volume_id, volume):
|
||||
return self.api_put('/volumes/%s' % volume_id, volume)['volume']
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# 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 unittest import skip
|
||||
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.tests.integrated import integrated_helpers
|
||||
|
@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class LoginTest(integrated_helpers._IntegratedTestBase):
|
||||
@skip('We do not have self.api.get_volumes()')
|
||||
def test_login(self):
|
||||
"""Simple check - we list volumes - so we know we're logged in."""
|
||||
volumes = self.api.get_volumes()
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# 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.
|
||||
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import service
|
||||
from manila.tests import fake_driver
|
||||
from manila.tests.integrated.api import client
|
||||
from manila.tests.integrated import integrated_helpers
|
||||
from manila.volume import driver
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VolumesTest(integrated_helpers._IntegratedTestBase):
|
||||
def setUp(self):
|
||||
super(VolumesTest, self).setUp()
|
||||
fake_driver.LoggingVolumeDriver.clear_logs()
|
||||
|
||||
def _start_api_service(self):
|
||||
self.osapi = service.WSGIService("osapi_share")
|
||||
self.osapi.start()
|
||||
self.auth_url = 'http://%s:%s/v1' % (self.osapi.host, self.osapi.port)
|
||||
LOG.warn(self.auth_url)
|
||||
|
||||
def _get_flags(self):
|
||||
f = super(VolumesTest, self)._get_flags()
|
||||
f['volume_driver'] = 'manila.tests.fake_driver.LoggingVolumeDriver'
|
||||
return f
|
||||
|
||||
def test_get_volumes_summary(self):
|
||||
"""Simple check that listing volumes works."""
|
||||
volumes = self.api.get_volumes(False)
|
||||
for volume in volumes:
|
||||
LOG.debug("volume: %s" % volume)
|
||||
|
||||
def test_get_volumes(self):
|
||||
"""Simple check that listing volumes works."""
|
||||
volumes = self.api.get_volumes()
|
||||
for volume in volumes:
|
||||
LOG.debug("volume: %s" % volume)
|
||||
|
||||
def _poll_while(self, volume_id, continue_states, max_retries=5):
|
||||
"""Poll (briefly) while the state is in continue_states."""
|
||||
retries = 0
|
||||
while True:
|
||||
try:
|
||||
found_volume = self.api.get_volume(volume_id)
|
||||
except client.OpenStackApiNotFoundException:
|
||||
found_volume = None
|
||||
LOG.debug("Got 404, proceeding")
|
||||
break
|
||||
|
||||
LOG.debug("Found %s" % found_volume)
|
||||
|
||||
self.assertEqual(volume_id, found_volume['id'])
|
||||
|
||||
if found_volume['status'] not in continue_states:
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
retries = retries + 1
|
||||
if retries > max_retries:
|
||||
break
|
||||
return found_volume
|
||||
|
||||
def test_create_and_delete_volume(self):
|
||||
"""Creates and deletes a volume."""
|
||||
|
||||
# Create volume
|
||||
created_volume = self.api.post_volume({'volume': {'size': 1}})
|
||||
LOG.debug("created_volume: %s" % created_volume)
|
||||
self.assertTrue(created_volume['id'])
|
||||
created_volume_id = created_volume['id']
|
||||
|
||||
# Check it's there
|
||||
found_volume = self.api.get_volume(created_volume_id)
|
||||
self.assertEqual(created_volume_id, found_volume['id'])
|
||||
|
||||
# It should also be in the all-volume list
|
||||
volumes = self.api.get_volumes()
|
||||
volume_names = [volume['id'] for volume in volumes]
|
||||
self.assertTrue(created_volume_id in volume_names)
|
||||
|
||||
# Wait (briefly) for creation. Delay is due to the 'message queue'
|
||||
found_volume = self._poll_while(created_volume_id, ['creating'])
|
||||
|
||||
# It should be available...
|
||||
self.assertEqual('available', found_volume['status'])
|
||||
|
||||
# Delete the volume
|
||||
self.api.delete_volume(created_volume_id)
|
||||
|
||||
# Wait (briefly) for deletion. Delay is due to the 'message queue'
|
||||
found_volume = self._poll_while(created_volume_id, ['deleting'])
|
||||
|
||||
# Should be gone
|
||||
self.assertFalse(found_volume)
|
||||
|
||||
LOG.debug("Logs: %s" % fake_driver.LoggingVolumeDriver.all_logs())
|
||||
|
||||
create_actions = fake_driver.LoggingVolumeDriver.logs_like(
|
||||
'create_volume',
|
||||
id=created_volume_id)
|
||||
LOG.debug("Create_Actions: %s" % create_actions)
|
||||
|
||||
self.assertEquals(1, len(create_actions))
|
||||
create_action = create_actions[0]
|
||||
self.assertEquals(create_action['id'], created_volume_id)
|
||||
self.assertEquals(create_action['availability_zone'], 'nova')
|
||||
self.assertEquals(create_action['size'], 1)
|
||||
|
||||
export_actions = fake_driver.LoggingVolumeDriver.logs_like(
|
||||
'create_export',
|
||||
id=created_volume_id)
|
||||
self.assertEquals(1, len(export_actions))
|
||||
export_action = export_actions[0]
|
||||
self.assertEquals(export_action['id'], created_volume_id)
|
||||
self.assertEquals(export_action['availability_zone'], 'nova')
|
||||
|
||||
delete_actions = fake_driver.LoggingVolumeDriver.logs_like(
|
||||
'delete_volume',
|
||||
id=created_volume_id)
|
||||
self.assertEquals(1, len(delete_actions))
|
||||
delete_action = export_actions[0]
|
||||
self.assertEquals(delete_action['id'], created_volume_id)
|
||||
|
||||
def test_create_volume_with_metadata(self):
|
||||
"""Creates a volume with metadata."""
|
||||
|
||||
# Create volume
|
||||
metadata = {'key1': 'value1',
|
||||
'key2': 'value2'}
|
||||
created_volume = self.api.post_volume(
|
||||
{'volume': {'size': 1,
|
||||
'metadata': metadata}})
|
||||
LOG.debug("created_volume: %s" % created_volume)
|
||||
self.assertTrue(created_volume['id'])
|
||||
created_volume_id = created_volume['id']
|
||||
|
||||
# Check it's there and metadata present
|
||||
found_volume = self.api.get_volume(created_volume_id)
|
||||
self.assertEqual(created_volume_id, found_volume['id'])
|
||||
self.assertEqual(metadata, found_volume['metadata'])
|
||||
|
||||
def test_create_volume_in_availability_zone(self):
|
||||
"""Creates a volume in availability_zone."""
|
||||
|
||||
# Create volume
|
||||
availability_zone = 'zone1:host1'
|
||||
created_volume = self.api.post_volume(
|
||||
{'volume': {'size': 1,
|
||||
'availability_zone': availability_zone}})
|
||||
LOG.debug("created_volume: %s" % created_volume)
|
||||
self.assertTrue(created_volume['id'])
|
||||
created_volume_id = created_volume['id']
|
||||
|
||||
# Check it's there and availability zone present
|
||||
found_volume = self.api.get_volume(created_volume_id)
|
||||
self.assertEqual(created_volume_id, found_volume['id'])
|
||||
self.assertEqual(availability_zone, found_volume['availability_zone'])
|
||||
|
||||
def test_create_and_update_volume(self):
|
||||
# Create vol1
|
||||
created_volume = self.api.post_volume({'volume': {
|
||||
'size': 1, 'display_name': 'vol1'}})
|
||||
self.assertEqual(created_volume['display_name'], 'vol1')
|
||||
created_volume_id = created_volume['id']
|
||||
|
||||
# update volume
|
||||
body = {'volume': {'display_name': 'vol-one'}}
|
||||
updated_volume = self.api.put_volume(created_volume_id, body)
|
||||
self.assertEqual(updated_volume['display_name'], 'vol-one')
|
||||
|
||||
# check for update
|
||||
found_volume = self.api.get_volume(created_volume_id)
|
||||
self.assertEqual(created_volume_id, found_volume['id'])
|
||||
self.assertEqual(found_volume['display_name'], 'vol-one')
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -37,81 +37,6 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
|
|||
|
||||
driver_cls = filter_scheduler.FilterScheduler
|
||||
|
||||
@test.skip_if(not test_utils.is_manila_installed(),
|
||||
'Test requires Manila installed (try setup.py develop')
|
||||
def test_create_volume_no_hosts(self):
|
||||
"""
|
||||
Ensure empty hosts & child_zones result in NoValidHosts exception.
|
||||
"""
|
||||
def _fake_empty_call_zone_method(*args, **kwargs):
|
||||
return []
|
||||
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
|
||||
fake_context = context.RequestContext('user', 'project')
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_id': ['fake-id1']}
|
||||
self.assertRaises(exception.NoValidHost, sched.schedule_create_volume,
|
||||
fake_context, request_spec, {})
|
||||
|
||||
@test.skip_if(not test_utils.is_manila_installed(),
|
||||
'Test requires Manila installed (try setup.py develop')
|
||||
def test_create_volume_non_admin(self):
|
||||
"""Test creating an instance locally using run_instance, passing
|
||||
a non-admin context. DB actions should work."""
|
||||
self.was_admin = False
|
||||
|
||||
def fake_get(context, *args, **kwargs):
|
||||
# make sure this is called with admin context, even though
|
||||
# we're using user context below
|
||||
self.was_admin = context.is_admin
|
||||
return {}
|
||||
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
self.stubs.Set(sched.host_manager, 'get_all_host_states', fake_get)
|
||||
|
||||
fake_context = context.RequestContext('user', 'project')
|
||||
|
||||
request_spec = {'volume_properties': {'project_id': 1,
|
||||
'size': 1},
|
||||
'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_id': ['fake-id1']}
|
||||
self.assertRaises(exception.NoValidHost, sched.schedule_create_volume,
|
||||
fake_context, request_spec, {})
|
||||
self.assertTrue(self.was_admin)
|
||||
|
||||
@test.skip_if(not test_utils.is_manila_installed(),
|
||||
'Test requires Manila installed (try setup.py develop')
|
||||
def test_schedule_happy_day(self):
|
||||
"""Make sure there's nothing glaringly wrong with _schedule()
|
||||
by doing a happy day pass through."""
|
||||
|
||||
self.next_weight = 1.0
|
||||
|
||||
def _fake_weigh_objects(_self, functions, hosts, options):
|
||||
self.next_weight += 2.0
|
||||
host_state = hosts[0]
|
||||
return [weights.WeighedHost(host_state, self.next_weight)]
|
||||
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
fake_context = context.RequestContext('user', 'project',
|
||||
is_admin=True)
|
||||
|
||||
self.stubs.Set(sched.host_manager, 'get_filtered_hosts',
|
||||
fake_get_filtered_hosts)
|
||||
self.stubs.Set(weights.HostWeightHandler,
|
||||
'get_weighed_objects', _fake_weigh_objects)
|
||||
fakes.mox_host_manager_db_calls(self.mox, fake_context)
|
||||
|
||||
request_spec = {'volume_type': {'name': 'LVM_iSCSI'},
|
||||
'volume_properties': {'project_id': 1,
|
||||
'size': 1}}
|
||||
self.mox.ReplayAll()
|
||||
weighed_host = sched._schedule(fake_context, request_spec, {})
|
||||
self.assertTrue(weighed_host.obj is not None)
|
||||
|
||||
@test.skip_if(not test_utils.is_manila_installed(),
|
||||
'Test requires Manila installed (try setup.py develop')
|
||||
def test_create_share_no_hosts(self):
|
||||
|
|
|
@ -222,43 +222,6 @@ class HostManagerTestCase(test.TestCase):
|
|||
class HostStateTestCase(test.TestCase):
|
||||
"""Test case for HostState class."""
|
||||
|
||||
def test_update_from_volume_capability(self):
|
||||
fake_host = host_manager.HostState('host1')
|
||||
self.assertEqual(fake_host.free_capacity_gb, None)
|
||||
|
||||
volume_capability = {'total_capacity_gb': 1024,
|
||||
'free_capacity_gb': 512,
|
||||
'reserved_percentage': 0,
|
||||
'timestamp': None}
|
||||
|
||||
fake_host.update_from_volume_capability(volume_capability)
|
||||
self.assertEqual(fake_host.free_capacity_gb, 512)
|
||||
|
||||
def test_update_from_volume_infinite_capability(self):
|
||||
fake_host = host_manager.HostState('host1')
|
||||
self.assertEqual(fake_host.free_capacity_gb, None)
|
||||
|
||||
volume_capability = {'total_capacity_gb': 'infinite',
|
||||
'free_capacity_gb': 'infinite',
|
||||
'reserved_percentage': 0,
|
||||
'timestamp': None}
|
||||
|
||||
fake_host.update_from_volume_capability(volume_capability)
|
||||
self.assertEqual(fake_host.total_capacity_gb, 'infinite')
|
||||
self.assertEqual(fake_host.free_capacity_gb, 'infinite')
|
||||
|
||||
def test_update_from_volume_unknown_capability(self):
|
||||
fake_host = host_manager.HostState('host1')
|
||||
self.assertEqual(fake_host.free_capacity_gb, None)
|
||||
|
||||
volume_capability = {'total_capacity_gb': 'infinite',
|
||||
'free_capacity_gb': 'unknown',
|
||||
'reserved_percentage': 0,
|
||||
'timestamp': None}
|
||||
|
||||
fake_host.update_from_volume_capability(volume_capability)
|
||||
self.assertEqual(fake_host.total_capacity_gb, 'infinite')
|
||||
self.assertEqual(fake_host.free_capacity_gb, 'unknown')
|
||||
|
||||
def test_update_from_share_capability(self):
|
||||
fake_host = host_manager.HostState('host1')
|
||||
|
|
|
@ -89,29 +89,6 @@ class SchedulerManagerTestCase(test.TestCase):
|
|||
service_name=service_name, host=host,
|
||||
capabilities=capabilities)
|
||||
|
||||
def test_create_volume_exception_puts_volume_in_error_state(self):
|
||||
"""Test that a NoValideHost exception for create_volume.
|
||||
|
||||
Puts the volume in 'error' state and eats the exception.
|
||||
"""
|
||||
fake_volume_id = 1
|
||||
self._mox_schedule_method_helper('schedule_create_volume')
|
||||
self.mox.StubOutWithMock(db, 'volume_update')
|
||||
|
||||
topic = 'fake_topic'
|
||||
volume_id = fake_volume_id
|
||||
request_spec = {'volume_id': fake_volume_id}
|
||||
|
||||
self.manager.driver.schedule_create_volume(
|
||||
self.context,
|
||||
request_spec, {}).AndRaise(exception.NoValidHost(reason=""))
|
||||
db.volume_update(self.context, fake_volume_id, {'status': 'error'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self.manager.create_volume(self.context, topic, volume_id,
|
||||
request_spec=request_spec,
|
||||
filter_properties={})
|
||||
|
||||
def test_create_share_exception_puts_share_in_error_state(self):
|
||||
"""Test that a NoValideHost exception for create_share.
|
||||
|
||||
|
@ -212,18 +189,6 @@ class SchedulerDriverModuleTestCase(test.TestCase):
|
|||
super(SchedulerDriverModuleTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
def test_volume_host_update_db(self):
|
||||
self.mox.StubOutWithMock(timeutils, 'utcnow')
|
||||
self.mox.StubOutWithMock(db, 'volume_update')
|
||||
|
||||
timeutils.utcnow().AndReturn('fake-now')
|
||||
db.volume_update(self.context, 31337,
|
||||
{'host': 'fake_host',
|
||||
'scheduled_at': 'fake-now'})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
driver.volume_update_db(self.context, 31337, 'fake_host')
|
||||
|
||||
def test_share_host_update_db(self):
|
||||
self.mox.StubOutWithMock(timeutils, 'utcnow')
|
||||
self.mox.StubOutWithMock(db, 'share_update')
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mox
|
||||
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.san.hp_lefthand import HpSanISCSIDriver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HpSanISCSITestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HpSanISCSITestCase, self).setUp()
|
||||
self.stubs.Set(HpSanISCSIDriver, "_cliq_run",
|
||||
self._fake_cliq_run)
|
||||
self.stubs.Set(HpSanISCSIDriver, "_get_iscsi_properties",
|
||||
self._fake_get_iscsi_properties)
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.san_is_local = False
|
||||
configuration.san_ip = "10.0.0.1"
|
||||
configuration.san_login = "foo"
|
||||
configuration.san_password = "bar"
|
||||
configuration.san_ssh_port = 16022
|
||||
configuration.san_clustername = "CloudCluster1"
|
||||
configuration.san_thin_provision = True
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
self.driver = HpSanISCSIDriver(configuration=configuration)
|
||||
self.volume_name = "fakevolume"
|
||||
self.connector = {'ip': '10.0.0.2',
|
||||
'initiator': 'iqn.1993-08.org.debian:01:222',
|
||||
'host': 'fakehost'}
|
||||
self.properties = {
|
||||
'target_discoverd': True,
|
||||
'target_portal': '10.0.1.6:3260',
|
||||
'target_iqn':
|
||||
'iqn.2003-10.com.lefthandnetworks:group01:25366:fakev',
|
||||
'volume_id': 1}
|
||||
|
||||
def tearDown(self):
|
||||
super(HpSanISCSITestCase, self).tearDown()
|
||||
|
||||
def _fake_get_iscsi_properties(self, volume):
|
||||
return self.properties
|
||||
|
||||
def _fake_cliq_run(self, verb, cliq_args, check_exit_code=True):
|
||||
"""Return fake results for the various methods."""
|
||||
|
||||
def create_volume(cliq_args):
|
||||
"""
|
||||
input = "createVolume description="fake description"
|
||||
clusterName=Cluster01 volumeName=fakevolume
|
||||
thinProvision=0 output=XML size=1GB"
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded."
|
||||
name="CliqSuccess" processingTime="181" result="0"/>
|
||||
</gauche>"""
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['thinProvision'], '1')
|
||||
self.assertEqual(cliq_args['size'], '1GB')
|
||||
return output, None
|
||||
|
||||
def delete_volume(cliq_args):
|
||||
"""
|
||||
input = "deleteVolume volumeName=fakevolume prompt=false
|
||||
output=XML"
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded."
|
||||
name="CliqSuccess" processingTime="164" result="0"/>
|
||||
</gauche>"""
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['prompt'], 'false')
|
||||
return output, None
|
||||
|
||||
def assign_volume(cliq_args):
|
||||
"""
|
||||
input = "assignVolumeToServer volumeName=fakevolume
|
||||
serverName=fakehost
|
||||
output=XML"
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded."
|
||||
name="CliqSuccess" processingTime="174" result="0"/>
|
||||
</gauche>"""
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['serverName'], self.connector['host'])
|
||||
return output, None
|
||||
|
||||
def unassign_volume(cliq_args):
|
||||
"""
|
||||
input = "unassignVolumeToServer volumeName=fakevolume
|
||||
serverName=fakehost output=XML
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded."
|
||||
name="CliqSuccess" processingTime="205" result="0"/>
|
||||
</gauche>"""
|
||||
self.assertEqual(cliq_args['volumeName'], self.volume_name)
|
||||
self.assertEqual(cliq_args['serverName'], self.connector['host'])
|
||||
return output, None
|
||||
|
||||
def get_cluster_info(cliq_args):
|
||||
"""
|
||||
input = "getClusterInfo clusterName=Cluster01 searchDepth=1
|
||||
verbose=0 output=XML"
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded." name="CliqSuccess"
|
||||
processingTime="1164" result="0">
|
||||
<cluster blockSize="1024" description=""
|
||||
maxVolumeSizeReplication1="622957690"
|
||||
maxVolumeSizeReplication2="311480287"
|
||||
minVolumeSize="262144" name="Cluster01"
|
||||
pageSize="262144" spaceTotal="633697992"
|
||||
storageNodeCount="2" unprovisionedSpace="622960574"
|
||||
useVip="true">
|
||||
<nsm ipAddress="10.0.1.7" name="111-vsa"/>
|
||||
<nsm ipAddress="10.0.1.8" name="112-vsa"/>
|
||||
<vip ipAddress="10.0.1.6" subnetMask="255.255.255.0"/>
|
||||
</cluster></response></gauche>"""
|
||||
return output, None
|
||||
|
||||
def get_volume_info(cliq_args):
|
||||
"""
|
||||
input = "getVolumeInfo volumeName=fakevolume output=XML"
|
||||
"""
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Operation succeeded." name="CliqSuccess"
|
||||
processingTime="87" result="0">
|
||||
<volume autogrowPages="4" availability="online"
|
||||
blockSize="1024" bytesWritten="0" checkSum="false"
|
||||
clusterName="Cluster01" created="2011-02-08T19:56:53Z"
|
||||
deleting="false" description="" groupName="Group01"
|
||||
initialQuota="536870912" isPrimary="true"
|
||||
iscsiIqn="iqn.2003-10.com.lefthandnetworks:group01:25366:fakev"
|
||||
maxSize="6865387257856" md5="9fa5c8b2cca54b2948a63d833097e1ca"
|
||||
minReplication="1" name="vol-b" parity="0" replication="2"
|
||||
reserveQuota="536870912" scratchQuota="4194304"
|
||||
serialNumber="9fa5c8b2cca54b2948a63d8"
|
||||
size="1073741824" stridePages="32" thinProvision="true">
|
||||
<status description="OK" value="2"/>
|
||||
<permission access="rw" authGroup="api-1"
|
||||
chapName="chapusername" chapRequired="true"
|
||||
id="25369" initiatorSecret="" iqn=""
|
||||
iscsiEnabled="true" loadBalance="true"
|
||||
targetSecret="supersecret"/>
|
||||
</volume></response></gauche>"""
|
||||
return output, None
|
||||
|
||||
def get_server_info(cliq_args):
|
||||
"""
|
||||
input = "getServerInfo serverName=fakeName"
|
||||
"""
|
||||
output = """<gauche version="1.0"><response result="0"/>
|
||||
</gauche>"""
|
||||
return output, None
|
||||
|
||||
def create_server(cliq_args):
|
||||
"""
|
||||
input = "createServer serverName=fakeName initiator=something"
|
||||
"""
|
||||
output = """<gauche version="1.0"><response result="0"/>
|
||||
</gauche>"""
|
||||
return output, None
|
||||
|
||||
def test_error(cliq_args):
|
||||
output = """<gauche version="1.0">
|
||||
<response description="Volume '134234' not found."
|
||||
name="CliqVolumeNotFound" processingTime="1083"
|
||||
result="8000100c"/>
|
||||
</gauche>"""
|
||||
return output, None
|
||||
|
||||
self.assertEqual(cliq_args['output'], 'XML')
|
||||
try:
|
||||
verbs = {'createVolume': create_volume,
|
||||
'deleteVolume': delete_volume,
|
||||
'assignVolumeToServer': assign_volume,
|
||||
'unassignVolumeToServer': unassign_volume,
|
||||
'getClusterInfo': get_cluster_info,
|
||||
'getVolumeInfo': get_volume_info,
|
||||
'getServerInfo': get_server_info,
|
||||
'createServer': create_server,
|
||||
'testError': test_error}
|
||||
except KeyError:
|
||||
raise NotImplementedError()
|
||||
|
||||
return verbs[verb](cliq_args)
|
||||
|
||||
def test_create_volume(self):
|
||||
volume = {'name': self.volume_name, 'size': 1}
|
||||
model_update = self.driver.create_volume(volume)
|
||||
expected_iqn = "iqn.2003-10.com.lefthandnetworks:group01:25366:fakev 0"
|
||||
expected_location = "10.0.1.6:3260,1 %s" % expected_iqn
|
||||
self.assertEqual(model_update['provider_location'], expected_location)
|
||||
|
||||
def test_delete_volume(self):
|
||||
volume = {'name': self.volume_name}
|
||||
self.driver.delete_volume(volume)
|
||||
|
||||
def test_initialize_connection(self):
|
||||
volume = {'name': self.volume_name}
|
||||
result = self.driver.initialize_connection(volume, self.connector)
|
||||
self.assertEqual(result['driver_volume_type'], 'iscsi')
|
||||
self.assertDictMatch(result['data'], self.properties)
|
||||
|
||||
def test_terminate_connection(self):
|
||||
volume = {'name': self.volume_name}
|
||||
self.driver.terminate_connection(volume, self.connector)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
try:
|
||||
self.driver.create_snapshot("")
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
try:
|
||||
self.driver.create_volume_from_snapshot("", "")
|
||||
except NotImplementedError:
|
||||
pass
|
||||
|
||||
def test_cliq_error(self):
|
||||
try:
|
||||
self.driver._cliq_run_xml("testError", {})
|
||||
except exception.ShareBackendAPIException:
|
||||
pass
|
|
@ -1,395 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
"""
|
||||
Tests for Backup code.
|
||||
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import importutils
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common import timeutils
|
||||
from manila import test
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeBackupException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BackupTestCase(test.TestCase):
|
||||
"""Test Case for backups."""
|
||||
|
||||
def setUp(self):
|
||||
super(BackupTestCase, self).setUp()
|
||||
vol_tmpdir = tempfile.mkdtemp()
|
||||
self.flags(connection_type='fake',
|
||||
volumes_dir=vol_tmpdir)
|
||||
self.backup_mgr = \
|
||||
importutils.import_object(FLAGS.backup_manager)
|
||||
self.backup_mgr.host = 'testhost'
|
||||
self.ctxt = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
super(BackupTestCase, self).tearDown()
|
||||
|
||||
def _create_backup_db_entry(self, volume_id=1, display_name='test_backup',
|
||||
display_description='this is a test backup',
|
||||
container='volumebackups',
|
||||
status='creating',
|
||||
size=0,
|
||||
object_count=0,
|
||||
project_id='fake'):
|
||||
"""
|
||||
Create a backup entry in the DB.
|
||||
Return the entry ID
|
||||
"""
|
||||
backup = {}
|
||||
backup['volume_id'] = volume_id
|
||||
backup['user_id'] = 'fake'
|
||||
backup['project_id'] = project_id
|
||||
backup['host'] = 'testhost'
|
||||
backup['availability_zone'] = '1'
|
||||
backup['display_name'] = display_name
|
||||
backup['display_description'] = display_description
|
||||
backup['container'] = container
|
||||
backup['status'] = status
|
||||
backup['fail_reason'] = ''
|
||||
backup['service'] = FLAGS.backup_service
|
||||
backup['size'] = size
|
||||
backup['object_count'] = object_count
|
||||
return db.backup_create(self.ctxt, backup)['id']
|
||||
|
||||
def _create_volume_db_entry(self, display_name='test_volume',
|
||||
display_description='this is a test volume',
|
||||
status='backing-up',
|
||||
size=1):
|
||||
"""
|
||||
Create a volume entry in the DB.
|
||||
Return the entry ID
|
||||
"""
|
||||
vol = {}
|
||||
vol['size'] = size
|
||||
vol['host'] = 'testhost'
|
||||
vol['user_id'] = 'fake'
|
||||
vol['project_id'] = 'fake'
|
||||
vol['status'] = status
|
||||
vol['display_name'] = display_name
|
||||
vol['display_description'] = display_description
|
||||
vol['attach_status'] = 'detached'
|
||||
return db.volume_create(self.ctxt, vol)['id']
|
||||
|
||||
def test_init_host(self):
|
||||
"""Make sure stuck volumes and backups are reset to correct
|
||||
states when backup_manager.init_host() is called"""
|
||||
vol1_id = self._create_volume_db_entry(status='backing-up')
|
||||
vol2_id = self._create_volume_db_entry(status='restoring-backup')
|
||||
backup1_id = self._create_backup_db_entry(status='creating')
|
||||
backup2_id = self._create_backup_db_entry(status='restoring')
|
||||
backup3_id = self._create_backup_db_entry(status='deleting')
|
||||
|
||||
self.backup_mgr.init_host()
|
||||
vol1 = db.volume_get(self.ctxt, vol1_id)
|
||||
self.assertEquals(vol1['status'], 'available')
|
||||
vol2 = db.volume_get(self.ctxt, vol2_id)
|
||||
self.assertEquals(vol2['status'], 'error_restoring')
|
||||
|
||||
backup1 = db.backup_get(self.ctxt, backup1_id)
|
||||
self.assertEquals(backup1['status'], 'error')
|
||||
backup2 = db.backup_get(self.ctxt, backup2_id)
|
||||
self.assertEquals(backup2['status'], 'available')
|
||||
self.assertRaises(exception.BackupNotFound,
|
||||
db.backup_get,
|
||||
self.ctxt,
|
||||
backup3_id)
|
||||
|
||||
def test_create_backup_with_bad_volume_status(self):
|
||||
"""Test error handling when creating a backup from a volume
|
||||
with a bad status"""
|
||||
vol_id = self._create_volume_db_entry(status='available', size=1)
|
||||
backup_id = self._create_backup_db_entry(volume_id=vol_id)
|
||||
self.assertRaises(exception.InvalidVolume,
|
||||
self.backup_mgr.create_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
|
||||
def test_create_backup_with_bad_backup_status(self):
|
||||
"""Test error handling when creating a backup with a backup
|
||||
with a bad status"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='available',
|
||||
volume_id=vol_id)
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
self.backup_mgr.create_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
|
||||
def test_create_backup_with_error(self):
|
||||
"""Test error handling when an error occurs during backup creation"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(volume_id=vol_id)
|
||||
|
||||
def fake_backup_volume(context, backup, backup_service):
|
||||
raise FakeBackupException('fake')
|
||||
|
||||
self.stubs.Set(self.backup_mgr.driver, 'backup_volume',
|
||||
fake_backup_volume)
|
||||
|
||||
self.assertRaises(FakeBackupException,
|
||||
self.backup_mgr.create_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'available')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'error')
|
||||
|
||||
def test_create_backup(self):
|
||||
"""Test normal backup creation"""
|
||||
vol_size = 1
|
||||
vol_id = self._create_volume_db_entry(size=vol_size)
|
||||
backup_id = self._create_backup_db_entry(volume_id=vol_id)
|
||||
|
||||
def fake_backup_volume(context, backup, backup_service):
|
||||
pass
|
||||
|
||||
self.stubs.Set(self.backup_mgr.driver, 'backup_volume',
|
||||
fake_backup_volume)
|
||||
|
||||
self.backup_mgr.create_backup(self.ctxt, backup_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'available')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'available')
|
||||
self.assertEqual(backup['size'], vol_size)
|
||||
|
||||
def test_restore_backup_with_bad_volume_status(self):
|
||||
"""Test error handling when restoring a backup to a volume
|
||||
with a bad status"""
|
||||
vol_id = self._create_volume_db_entry(status='available', size=1)
|
||||
backup_id = self._create_backup_db_entry(volume_id=vol_id)
|
||||
self.assertRaises(exception.InvalidVolume,
|
||||
self.backup_mgr.restore_backup,
|
||||
self.ctxt,
|
||||
backup_id,
|
||||
vol_id)
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'available')
|
||||
|
||||
def test_restore_backup_with_bad_backup_status(self):
|
||||
"""Test error handling when restoring a backup with a backup
|
||||
with a bad status"""
|
||||
vol_id = self._create_volume_db_entry(status='restoring-backup',
|
||||
size=1)
|
||||
backup_id = self._create_backup_db_entry(status='available',
|
||||
volume_id=vol_id)
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
self.backup_mgr.restore_backup,
|
||||
self.ctxt,
|
||||
backup_id,
|
||||
vol_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'error')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'error')
|
||||
|
||||
def test_restore_backup_with_driver_error(self):
|
||||
"""Test error handling when an error occurs during backup restore"""
|
||||
vol_id = self._create_volume_db_entry(status='restoring-backup',
|
||||
size=1)
|
||||
backup_id = self._create_backup_db_entry(status='restoring',
|
||||
volume_id=vol_id)
|
||||
|
||||
def fake_restore_backup(context, backup, volume, backup_service):
|
||||
raise FakeBackupException('fake')
|
||||
|
||||
self.stubs.Set(self.backup_mgr.driver, 'restore_backup',
|
||||
fake_restore_backup)
|
||||
|
||||
self.assertRaises(FakeBackupException,
|
||||
self.backup_mgr.restore_backup,
|
||||
self.ctxt,
|
||||
backup_id,
|
||||
vol_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'error_restoring')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'available')
|
||||
|
||||
def test_restore_backup_with_bad_service(self):
|
||||
"""Test error handling when attempting a restore of a backup
|
||||
with a different service to that used to create the backup"""
|
||||
vol_id = self._create_volume_db_entry(status='restoring-backup',
|
||||
size=1)
|
||||
backup_id = self._create_backup_db_entry(status='restoring',
|
||||
volume_id=vol_id)
|
||||
|
||||
def fake_restore_backup(context, backup, volume, backup_service):
|
||||
pass
|
||||
|
||||
self.stubs.Set(self.backup_mgr.driver, 'restore_backup',
|
||||
fake_restore_backup)
|
||||
|
||||
service = 'manila.tests.backup.bad_service'
|
||||
db.backup_update(self.ctxt, backup_id, {'service': service})
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
self.backup_mgr.restore_backup,
|
||||
self.ctxt,
|
||||
backup_id,
|
||||
vol_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'error')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'available')
|
||||
|
||||
def test_restore_backup(self):
|
||||
"""Test normal backup restoration"""
|
||||
vol_size = 1
|
||||
vol_id = self._create_volume_db_entry(status='restoring-backup',
|
||||
size=vol_size)
|
||||
backup_id = self._create_backup_db_entry(status='restoring',
|
||||
volume_id=vol_id)
|
||||
|
||||
def fake_restore_backup(context, backup, volume, backup_service):
|
||||
pass
|
||||
|
||||
self.stubs.Set(self.backup_mgr.driver, 'restore_backup',
|
||||
fake_restore_backup)
|
||||
|
||||
self.backup_mgr.restore_backup(self.ctxt, backup_id, vol_id)
|
||||
vol = db.volume_get(self.ctxt, vol_id)
|
||||
self.assertEquals(vol['status'], 'available')
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'available')
|
||||
|
||||
def test_delete_backup_with_bad_backup_status(self):
|
||||
"""Test error handling when deleting a backup with a backup
|
||||
with a bad status"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='available',
|
||||
volume_id=vol_id)
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
self.backup_mgr.delete_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'error')
|
||||
|
||||
def test_delete_backup_with_error(self):
|
||||
"""Test error handling when an error occurs during backup deletion."""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='deleting',
|
||||
display_name='fail_on_delete',
|
||||
volume_id=vol_id)
|
||||
self.assertRaises(IOError,
|
||||
self.backup_mgr.delete_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'error')
|
||||
|
||||
def test_delete_backup_with_bad_service(self):
|
||||
"""Test error handling when attempting a delete of a backup
|
||||
with a different service to that used to create the backup"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='deleting',
|
||||
volume_id=vol_id)
|
||||
service = 'manila.tests.backup.bad_service'
|
||||
db.backup_update(self.ctxt, backup_id, {'service': service})
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
self.backup_mgr.delete_backup,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
backup = db.backup_get(self.ctxt, backup_id)
|
||||
self.assertEquals(backup['status'], 'error')
|
||||
|
||||
def test_delete_backup_with_no_service(self):
|
||||
"""Test error handling when attempting a delete of a backup
|
||||
with no service defined for that backup, relates to bug #1162908"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='deleting',
|
||||
volume_id=vol_id)
|
||||
db.backup_update(self.ctxt, backup_id, {'service': None})
|
||||
self.backup_mgr.delete_backup(self.ctxt, backup_id)
|
||||
|
||||
def test_delete_backup(self):
|
||||
"""Test normal backup deletion"""
|
||||
vol_id = self._create_volume_db_entry(size=1)
|
||||
backup_id = self._create_backup_db_entry(status='deleting',
|
||||
volume_id=vol_id)
|
||||
self.backup_mgr.delete_backup(self.ctxt, backup_id)
|
||||
self.assertRaises(exception.BackupNotFound,
|
||||
db.backup_get,
|
||||
self.ctxt,
|
||||
backup_id)
|
||||
|
||||
ctxt_read_deleted = context.get_admin_context('yes')
|
||||
backup = db.backup_get(ctxt_read_deleted, backup_id)
|
||||
self.assertEqual(backup.deleted, True)
|
||||
self.assertTrue(timeutils.utcnow() > backup.deleted_at)
|
||||
self.assertEqual(backup.status, 'deleted')
|
||||
|
||||
def test_list_backup(self):
|
||||
backups = db.backup_get_all_by_project(self.ctxt, 'project1')
|
||||
self.assertEqual(len(backups), 0)
|
||||
|
||||
b1 = self._create_backup_db_entry()
|
||||
b2 = self._create_backup_db_entry(project_id='project1')
|
||||
backups = db.backup_get_all_by_project(self.ctxt, 'project1')
|
||||
self.assertEqual(len(backups), 1)
|
||||
self.assertEqual(backups[0].id, b2)
|
||||
|
||||
def test_backup_get_all_by_project_with_deleted(self):
|
||||
"""Test deleted backups don't show up in backup_get_all_by_project.
|
||||
Unless context.read_deleted is 'yes'"""
|
||||
backups = db.backup_get_all_by_project(self.ctxt, 'fake')
|
||||
self.assertEqual(len(backups), 0)
|
||||
|
||||
backup_id_keep = self._create_backup_db_entry()
|
||||
backup_id = self._create_backup_db_entry()
|
||||
db.backup_destroy(self.ctxt, backup_id)
|
||||
|
||||
backups = db.backup_get_all_by_project(self.ctxt, 'fake')
|
||||
self.assertEqual(len(backups), 1)
|
||||
self.assertEqual(backups[0].id, backup_id_keep)
|
||||
|
||||
ctxt_read_deleted = context.get_admin_context('yes')
|
||||
backups = db.backup_get_all_by_project(ctxt_read_deleted, 'fake')
|
||||
self.assertEqual(len(backups), 2)
|
||||
|
||||
def test_backup_get_all_by_host_with_deleted(self):
|
||||
"""Test deleted backups don't show up in backup_get_all_by_project.
|
||||
Unless context.read_deleted is 'yes'"""
|
||||
backups = db.backup_get_all_by_host(self.ctxt, 'testhost')
|
||||
self.assertEqual(len(backups), 0)
|
||||
|
||||
backup_id_keep = self._create_backup_db_entry()
|
||||
backup_id = self._create_backup_db_entry()
|
||||
db.backup_destroy(self.ctxt, backup_id)
|
||||
|
||||
backups = db.backup_get_all_by_host(self.ctxt, 'testhost')
|
||||
self.assertEqual(len(backups), 1)
|
||||
self.assertEqual(backups[0].id, backup_id_keep)
|
||||
|
||||
ctxt_read_deleted = context.get_admin_context('yes')
|
||||
backups = db.backup_get_all_by_host(ctxt_read_deleted, 'testhost')
|
||||
self.assertEqual(len(backups), 2)
|
|
@ -1,208 +0,0 @@
|
|||
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
"""
|
||||
Tests for Backup swift code.
|
||||
|
||||
"""
|
||||
|
||||
import bz2
|
||||
import hashlib
|
||||
import os
|
||||
import tempfile
|
||||
import zlib
|
||||
|
||||
from manila.backup.services.swift import SwiftBackupService
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.tests.backup.fake_swift_client import FakeSwiftClient
|
||||
from swiftclient import client as swift
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def fake_md5(arg):
|
||||
class result(object):
|
||||
def hexdigest(self):
|
||||
return 'fake-md5-sum'
|
||||
|
||||
ret = result()
|
||||
return ret
|
||||
|
||||
|
||||
class BackupSwiftTestCase(test.TestCase):
|
||||
"""Test Case for swift."""
|
||||
|
||||
def _create_volume_db_entry(self):
|
||||
vol = {'id': '1234-5678-1234-8888',
|
||||
'size': 1,
|
||||
'status': 'available'}
|
||||
return db.volume_create(self.ctxt, vol)['id']
|
||||
|
||||
def _create_backup_db_entry(self, container='test-container'):
|
||||
backup = {'id': 123,
|
||||
'size': 1,
|
||||
'container': container,
|
||||
'volume_id': '1234-5678-1234-8888'}
|
||||
return db.backup_create(self.ctxt, backup)['id']
|
||||
|
||||
def setUp(self):
|
||||
super(BackupSwiftTestCase, self).setUp()
|
||||
self.ctxt = context.get_admin_context()
|
||||
|
||||
self.stubs.Set(swift, 'Connection', FakeSwiftClient.Connection)
|
||||
self.stubs.Set(hashlib, 'md5', fake_md5)
|
||||
|
||||
self._create_volume_db_entry()
|
||||
self.volume_file = tempfile.NamedTemporaryFile()
|
||||
for i in xrange(0, 128):
|
||||
self.volume_file.write(os.urandom(1024))
|
||||
|
||||
def tearDown(self):
|
||||
self.volume_file.close()
|
||||
super(BackupSwiftTestCase, self).tearDown()
|
||||
|
||||
def test_backup_uncompressed(self):
|
||||
self._create_backup_db_entry()
|
||||
self.flags(backup_compression_algorithm='none')
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.backup(backup, self.volume_file)
|
||||
|
||||
def test_backup_bz2(self):
|
||||
self._create_backup_db_entry()
|
||||
self.flags(backup_compression_algorithm='bz2')
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.backup(backup, self.volume_file)
|
||||
|
||||
def test_backup_zlib(self):
|
||||
self._create_backup_db_entry()
|
||||
self.flags(backup_compression_algorithm='zlib')
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.backup(backup, self.volume_file)
|
||||
|
||||
def test_backup_default_container(self):
|
||||
self._create_backup_db_entry(container=None)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.backup(backup, self.volume_file)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertEquals(backup['container'], 'volumebackups')
|
||||
|
||||
def test_backup_custom_container(self):
|
||||
container_name = 'fake99'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.backup(backup, self.volume_file)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertEquals(backup['container'], container_name)
|
||||
|
||||
def test_create_backup_container_check_wraps_socket_error(self):
|
||||
container_name = 'socket_error_on_head'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertRaises(exception.SwiftConnectionFailed,
|
||||
service.backup,
|
||||
backup, self.volume_file)
|
||||
|
||||
def test_create_backup_put_object_wraps_socket_error(self):
|
||||
container_name = 'socket_error_on_put'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
self.volume_file.seek(0)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertRaises(exception.SwiftConnectionFailed,
|
||||
service.backup,
|
||||
backup, self.volume_file)
|
||||
|
||||
def test_restore(self):
|
||||
self._create_backup_db_entry()
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as volume_file:
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.restore(backup, '1234-5678-1234-8888', volume_file)
|
||||
|
||||
def test_restore_wraps_socket_error(self):
|
||||
container_name = 'socket_error_on_get'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as volume_file:
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertRaises(exception.SwiftConnectionFailed,
|
||||
service.restore,
|
||||
backup, '1234-5678-1234-8888', volume_file)
|
||||
|
||||
def test_restore_unsupported_version(self):
|
||||
container_name = 'unsupported_version'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as volume_file:
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertRaises(exception.InvalidBackup,
|
||||
service.restore,
|
||||
backup, '1234-5678-1234-8888', volume_file)
|
||||
|
||||
def test_delete(self):
|
||||
self._create_backup_db_entry()
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
service.delete(backup)
|
||||
|
||||
def test_delete_wraps_socket_error(self):
|
||||
container_name = 'socket_error_on_delete'
|
||||
self._create_backup_db_entry(container=container_name)
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
backup = db.backup_get(self.ctxt, 123)
|
||||
self.assertRaises(exception.SwiftConnectionFailed,
|
||||
service.delete,
|
||||
backup)
|
||||
|
||||
def test_get_compressor(self):
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
compressor = service._get_compressor('None')
|
||||
self.assertEquals(compressor, None)
|
||||
compressor = service._get_compressor('zlib')
|
||||
self.assertEquals(compressor, zlib)
|
||||
compressor = service._get_compressor('bz2')
|
||||
self.assertEquals(compressor, bz2)
|
||||
self.assertRaises(ValueError, service._get_compressor, 'fake')
|
||||
|
||||
def test_check_container_exists(self):
|
||||
service = SwiftBackupService(self.ctxt)
|
||||
exists = service._check_container_exists('fake_container')
|
||||
self.assertEquals(exists, True)
|
||||
exists = service._check_container_exists('missing_container')
|
||||
self.assertEquals(exists, False)
|
||||
self.assertRaises(swift.ClientException,
|
||||
service._check_container_exists,
|
||||
'unauthorized_container')
|
|
@ -1,268 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import mox
|
||||
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers import coraid
|
||||
from manila.volume.drivers.coraid import CoraidDriver
|
||||
from manila.volume.drivers.coraid import CoraidESMException
|
||||
from manila.volume.drivers.coraid import CoraidRESTClient
|
||||
|
||||
import cookielib
|
||||
import urllib2
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
fake_esm_ipaddress = "192.168.0.1"
|
||||
fake_esm_username = "darmok"
|
||||
fake_esm_group = "tanagra"
|
||||
fake_esm_password = "12345678"
|
||||
|
||||
fake_volume_name = "volume-12345678-1234-1234-1234-1234567890ab"
|
||||
fake_volume_size = "10"
|
||||
fake_repository_name = "A-B:C:D"
|
||||
fake_pool_name = "FakePool"
|
||||
fake_aoetarget = 4081
|
||||
fake_shelf = 16
|
||||
fake_lun = 241
|
||||
|
||||
fake_str_aoetarget = str(fake_aoetarget)
|
||||
fake_lun_addr = {"shelf": fake_shelf, "lun": fake_lun}
|
||||
|
||||
fake_volume = {"name": fake_volume_name,
|
||||
"size": fake_volume_size,
|
||||
"volume_type": {"id": 1}}
|
||||
|
||||
fake_volume_info = {"pool": fake_pool_name,
|
||||
"repo": fake_repository_name,
|
||||
"vsxidx": fake_aoetarget,
|
||||
"index": fake_lun,
|
||||
"shelf": fake_shelf}
|
||||
|
||||
fake_lun_info = {"shelf": fake_shelf, "lun": fake_lun}
|
||||
|
||||
fake_snapshot_name = "snapshot-12345678-8888-8888-1234-1234567890ab"
|
||||
fake_snapshot_id = "12345678-8888-8888-1234-1234567890ab"
|
||||
fake_volume_id = "12345678-1234-1234-1234-1234567890ab"
|
||||
fake_snapshot = {"id": fake_snapshot_id,
|
||||
"volume_id": fake_volume_id}
|
||||
|
||||
fake_configure_data = [{"addr": "cms", "data": "FAKE"}]
|
||||
|
||||
fake_esm_fetch = [[
|
||||
{"command": "super_fake_command_of_death"},
|
||||
{"reply": [
|
||||
{"lv":
|
||||
{"containingPool": fake_pool_name,
|
||||
"lunIndex": fake_aoetarget,
|
||||
"name": fake_volume_name,
|
||||
"lvStatus":
|
||||
{"exportedLun":
|
||||
{"lun": fake_lun,
|
||||
"shelf": fake_shelf}}
|
||||
},
|
||||
"repoName": fake_repository_name}]}]]
|
||||
|
||||
fake_esm_success = {"category": "provider",
|
||||
"tracking": False,
|
||||
"configState": "completedSuccessfully",
|
||||
"heldPending": False,
|
||||
"metaCROp": "noAction",
|
||||
"message": None}
|
||||
|
||||
fake_group_fullpath = "admin group:%s" % (fake_esm_group)
|
||||
fake_group_id = 4
|
||||
fake_login_reply = {"values": [
|
||||
{"fullPath": fake_group_fullpath,
|
||||
"groupId": fake_group_id}],
|
||||
"message": "",
|
||||
"state": "adminSucceed",
|
||||
"metaCROp": "noAction"}
|
||||
|
||||
fake_group_fail_fullpath = "fail group:%s" % (fake_esm_group)
|
||||
fake_group_fail_id = 5
|
||||
fake_login_reply_group_fail = {"values": [
|
||||
{"fullPath": fake_group_fail_fullpath,
|
||||
"groupId": fake_group_fail_id}],
|
||||
"message": "",
|
||||
"state": "adminSucceed",
|
||||
"metaCROp": "noAction"}
|
||||
|
||||
|
||||
class TestCoraidDriver(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCoraidDriver, self).setUp()
|
||||
self.esm_mock = self.mox.CreateMockAnything()
|
||||
self.stubs.Set(coraid, 'CoraidRESTClient',
|
||||
lambda *_, **__: self.esm_mock)
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
configuration.coraid_esm_address = fake_esm_ipaddress
|
||||
configuration.coraid_user = fake_esm_username
|
||||
configuration.coraid_group = fake_esm_group
|
||||
configuration.coraid_password = fake_esm_password
|
||||
|
||||
self.drv = CoraidDriver(configuration=configuration)
|
||||
self.drv.do_setup({})
|
||||
|
||||
def test_create_volume(self):
|
||||
setattr(self.esm_mock, 'create_lun', lambda *_: True)
|
||||
self.stubs.Set(CoraidDriver, '_get_repository',
|
||||
lambda *_: fake_repository_name)
|
||||
self.drv.create_volume(fake_volume)
|
||||
|
||||
def test_delete_volume(self):
|
||||
setattr(self.esm_mock, 'delete_lun',
|
||||
lambda *_: True)
|
||||
self.drv.delete_volume(fake_volume)
|
||||
|
||||
def test_initialize_connection(self):
|
||||
setattr(self.esm_mock, '_get_lun_address',
|
||||
lambda *_: fake_lun_addr)
|
||||
self.drv.initialize_connection(fake_volume, '')
|
||||
|
||||
def test_create_snapshot(self):
|
||||
setattr(self.esm_mock, 'create_snapshot',
|
||||
lambda *_: True)
|
||||
self.drv.create_snapshot(fake_snapshot)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
setattr(self.esm_mock, 'delete_snapshot',
|
||||
lambda *_: True)
|
||||
self.drv.delete_snapshot(fake_snapshot)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
setattr(self.esm_mock, 'create_volume_from_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidDriver, '_get_repository',
|
||||
lambda *_: fake_repository_name)
|
||||
self.drv.create_volume_from_snapshot(fake_volume, fake_snapshot)
|
||||
|
||||
|
||||
class TestCoraidRESTClient(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCoraidRESTClient, self).setUp()
|
||||
self.stubs.Set(cookielib, 'CookieJar', lambda *_: True)
|
||||
self.stubs.Set(urllib2, 'build_opener', lambda *_: True)
|
||||
self.stubs.Set(urllib2, 'HTTPCookieProcessor', lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_login', lambda *_: True)
|
||||
self.rest_mock = self.mox.CreateMockAnything()
|
||||
self.stubs.Set(coraid, 'CoraidRESTClient',
|
||||
lambda *_, **__: self.rest_mock)
|
||||
self.drv = CoraidRESTClient(fake_esm_ipaddress,
|
||||
fake_esm_username,
|
||||
fake_esm_group,
|
||||
fake_esm_password)
|
||||
|
||||
def test__get_group_id(self):
|
||||
setattr(self.rest_mock, '_get_group_id',
|
||||
lambda *_: True)
|
||||
self.assertEquals(self.drv._get_group_id(fake_esm_group,
|
||||
fake_login_reply),
|
||||
fake_group_id)
|
||||
|
||||
def test__set_group(self):
|
||||
setattr(self.rest_mock, '_set_group',
|
||||
lambda *_: fake_group_id)
|
||||
self.stubs.Set(CoraidRESTClient, '_admin_esm_cmd',
|
||||
lambda *_: fake_login_reply)
|
||||
self.drv._set_group(fake_login_reply)
|
||||
|
||||
def test__set_group_fails_no_group(self):
|
||||
setattr(self.rest_mock, '_set_group',
|
||||
lambda *_: False)
|
||||
self.stubs.Set(CoraidRESTClient, '_admin_esm_cmd',
|
||||
lambda *_: fake_login_reply_group_fail)
|
||||
self.assertRaises(CoraidESMException,
|
||||
self.drv._set_group,
|
||||
fake_login_reply_group_fail)
|
||||
|
||||
def test__configure(self):
|
||||
setattr(self.rest_mock, '_configure',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_esm_cmd',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv._configure(fake_configure_data)
|
||||
|
||||
def test__get_volume_info(self):
|
||||
setattr(self.rest_mock, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_esm_cmd',
|
||||
lambda *_: fake_esm_fetch)
|
||||
self.drv._get_volume_info(fake_volume_name)
|
||||
|
||||
def test__get_lun_address(self):
|
||||
setattr(self.rest_mock, '_get_lun_address',
|
||||
lambda *_: fake_lun_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.drv._get_lun_address(fake_volume_name)
|
||||
|
||||
def test_create_lun(self):
|
||||
setattr(self.rest_mock, 'create_lun',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.rest_mock.create_lun(fake_volume_name, '10',
|
||||
fake_repository_name)
|
||||
self.drv.create_lun(fake_volume_name, '10',
|
||||
fake_repository_name)
|
||||
|
||||
def test_delete_lun(self):
|
||||
setattr(self.rest_mock, 'delete_lun',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.rest_mock.delete_lun(fake_volume_name)
|
||||
self.drv.delete_lun(fake_volume_name)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
setattr(self.rest_mock, 'create_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.create_snapshot(fake_volume_name,
|
||||
fake_volume_name)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
setattr(self.rest_mock, 'delete_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.delete_snapshot(fake_volume_name)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
setattr(self.rest_mock, 'create_volume_from_snapshot',
|
||||
lambda *_: True)
|
||||
self.stubs.Set(CoraidRESTClient, '_get_volume_info',
|
||||
lambda *_: fake_volume_info)
|
||||
self.stubs.Set(CoraidRESTClient, '_configure',
|
||||
lambda *_: fake_esm_success)
|
||||
self.drv.create_volume_from_snapshot(fake_volume_name,
|
||||
fake_volume_name,
|
||||
fake_repository_name)
|
|
@ -1,184 +0,0 @@
|
|||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# 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 manila import context
|
||||
from manila import flags
|
||||
from manila.openstack.common import importutils
|
||||
from manila import test
|
||||
from manila.volume.drivers.solidfire import SolidFire
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
RBD_MODULE = "manila.volume.drivers.rbd.RBDDriver"
|
||||
SHEEPDOG_MODULE = "manila.volume.drivers.sheepdog.SheepdogDriver"
|
||||
NEXENTA_MODULE = "manila.volume.drivers.nexenta.volume.NexentaDriver"
|
||||
SAN_MODULE = "manila.volume.drivers.san.san.SanISCSIDriver"
|
||||
SOLARIS_MODULE = "manila.volume.drivers.san.solaris.SolarisISCSIDriver"
|
||||
LEFTHAND_MODULE = "manila.volume.drivers.san.hp_lefthand.HpSanISCSIDriver"
|
||||
NETAPP_MODULE = "manila.volume.drivers.netapp.iscsi.NetAppISCSIDriver"
|
||||
NETAPP_CMODE_MODULE =\
|
||||
"manila.volume.drivers.netapp.iscsi.NetAppCmodeISCSIDriver"
|
||||
NETAPP_NFS_MODULE = "manila.volume.drivers.netapp.nfs.NetAppNFSDriver"
|
||||
NFS_MODULE = "manila.volume.drivers.nfs.NfsDriver"
|
||||
SOLIDFIRE_MODULE = "manila.volume.drivers.solidfire.SolidFire"
|
||||
STORWIZE_SVC_MODULE = "manila.volume.drivers.storwize_svc.StorwizeSVCDriver"
|
||||
WINDOWS_MODULE = "manila.volume.drivers.windows.WindowsDriver"
|
||||
XIV_MODULE = "manila.volume.drivers.xiv.XIVDriver"
|
||||
ZADARA_MODULE = "manila.volume.drivers.zadara.ZadaraVPSAISCSIDriver"
|
||||
|
||||
|
||||
class VolumeDriverCompatibility(test.TestCase):
|
||||
"""Test backwards compatibility for volume drivers."""
|
||||
|
||||
def fake_update_cluster_status(self):
|
||||
return
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeDriverCompatibility, self).setUp()
|
||||
self.manager = importutils.import_object(FLAGS.volume_manager)
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
super(VolumeDriverCompatibility, self).tearDown()
|
||||
|
||||
def _load_driver(self, driver):
|
||||
if 'SolidFire' in driver:
|
||||
# SolidFire driver does update_cluster stat on init
|
||||
self.stubs.Set(SolidFire, '_update_cluster_status',
|
||||
self.fake_update_cluster_status)
|
||||
self.manager.__init__(volume_driver=driver)
|
||||
|
||||
def _driver_module_name(self):
|
||||
return "%s.%s" % (self.manager.driver.__class__.__module__,
|
||||
self.manager.driver.__class__.__name__)
|
||||
|
||||
def test_rbd_old(self):
|
||||
self._load_driver('manila.volume.driver.RBDDriver')
|
||||
self.assertEquals(self._driver_module_name(), RBD_MODULE)
|
||||
|
||||
def test_rbd_new(self):
|
||||
self._load_driver(RBD_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), RBD_MODULE)
|
||||
|
||||
def test_sheepdog_old(self):
|
||||
self._load_driver('manila.volume.driver.SheepdogDriver')
|
||||
self.assertEquals(self._driver_module_name(), SHEEPDOG_MODULE)
|
||||
|
||||
def test_sheepdog_new(self):
|
||||
self._load_driver(SHEEPDOG_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), SHEEPDOG_MODULE)
|
||||
|
||||
def test_nexenta_old(self):
|
||||
self._load_driver('manila.volume.nexenta.volume.NexentaDriver')
|
||||
self.assertEquals(self._driver_module_name(), NEXENTA_MODULE)
|
||||
|
||||
def test_nexenta_new(self):
|
||||
self._load_driver(NEXENTA_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NEXENTA_MODULE)
|
||||
|
||||
def test_san_old(self):
|
||||
self._load_driver('manila.volume.san.SanISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), SAN_MODULE)
|
||||
|
||||
def test_san_new(self):
|
||||
self._load_driver(SAN_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), SAN_MODULE)
|
||||
|
||||
def test_solaris_old(self):
|
||||
self._load_driver('manila.volume.san.SolarisISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), SOLARIS_MODULE)
|
||||
|
||||
def test_solaris_new(self):
|
||||
self._load_driver(SOLARIS_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), SOLARIS_MODULE)
|
||||
|
||||
def test_hp_lefthand_old(self):
|
||||
self._load_driver('manila.volume.san.HpSanISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), LEFTHAND_MODULE)
|
||||
|
||||
def test_hp_lefthand_new(self):
|
||||
self._load_driver(LEFTHAND_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), LEFTHAND_MODULE)
|
||||
|
||||
def test_netapp_old(self):
|
||||
self._load_driver('manila.volume.netapp.NetAppISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_MODULE)
|
||||
|
||||
def test_netapp_new(self):
|
||||
self._load_driver(NETAPP_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_MODULE)
|
||||
|
||||
def test_netapp_cmode_old(self):
|
||||
self._load_driver('manila.volume.netapp.NetAppCmodeISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_CMODE_MODULE)
|
||||
|
||||
def test_netapp_cmode_new(self):
|
||||
self._load_driver(NETAPP_CMODE_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_CMODE_MODULE)
|
||||
|
||||
def test_netapp_nfs_old(self):
|
||||
self._load_driver('manila.volume.netapp_nfs.NetAppNFSDriver')
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_NFS_MODULE)
|
||||
|
||||
def test_netapp_nfs_new(self):
|
||||
self._load_driver(NETAPP_NFS_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NETAPP_NFS_MODULE)
|
||||
|
||||
def test_nfs_old(self):
|
||||
self._load_driver('manila.volume.nfs.NfsDriver')
|
||||
self.assertEquals(self._driver_module_name(), NFS_MODULE)
|
||||
|
||||
def test_nfs_new(self):
|
||||
self._load_driver(NFS_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), NFS_MODULE)
|
||||
|
||||
def test_solidfire_old(self):
|
||||
self._load_driver('manila.volume.solidfire.SolidFire')
|
||||
self.assertEquals(self._driver_module_name(), SOLIDFIRE_MODULE)
|
||||
|
||||
def test_solidfire_new(self):
|
||||
self._load_driver(SOLIDFIRE_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), SOLIDFIRE_MODULE)
|
||||
|
||||
def test_storwize_svc_old(self):
|
||||
self._load_driver('manila.volume.storwize_svc.StorwizeSVCDriver')
|
||||
self.assertEquals(self._driver_module_name(), STORWIZE_SVC_MODULE)
|
||||
|
||||
def test_storwize_svc_new(self):
|
||||
self._load_driver(STORWIZE_SVC_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), STORWIZE_SVC_MODULE)
|
||||
|
||||
def test_windows_old(self):
|
||||
self._load_driver('manila.volume.windows.WindowsDriver')
|
||||
self.assertEquals(self._driver_module_name(), WINDOWS_MODULE)
|
||||
|
||||
def test_windows_new(self):
|
||||
self._load_driver(WINDOWS_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), WINDOWS_MODULE)
|
||||
|
||||
def test_xiv_old(self):
|
||||
self._load_driver('manila.volume.xiv.XIVDriver')
|
||||
self.assertEquals(self._driver_module_name(), XIV_MODULE)
|
||||
|
||||
def test_xiv_new(self):
|
||||
self._load_driver(XIV_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), XIV_MODULE)
|
||||
|
||||
def test_zadara_old(self):
|
||||
self._load_driver('manila.volume.zadara.ZadaraVPSAISCSIDriver')
|
||||
self.assertEquals(self._driver_module_name(), ZADARA_MODULE)
|
||||
|
||||
def test_zadara_new(self):
|
||||
self._load_driver(ZADARA_MODULE)
|
||||
self.assertEquals(self._driver_module_name(), ZADARA_MODULE)
|
|
@ -1,766 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 EMC Corporation, Inc.
|
||||
# Copyright (c) 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.
|
||||
|
||||
import mox
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from xml.dom.minidom import Document
|
||||
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.emc.emc_smis_common import EMCSMISCommon
|
||||
from manila.volume.drivers.emc.emc_smis_iscsi import EMCSMISISCSIDriver
|
||||
|
||||
MANILA_EMC_CONFIG_FILE = '/etc/manila/manila_emc_config.xml'
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
config_file_name = 'manila_emc_config.xml'
|
||||
storage_system = 'CLARiiON+APM00123456789'
|
||||
storage_system_vmax = 'SYMMETRIX+000195900551'
|
||||
lunmaskctrl_id = 'CLARiiON+APM00123456789+00aa11bb22cc33dd44ff55gg66hh77ii88jj'
|
||||
initiator1 = 'iqn.1993-08.org.debian:01:1a2b3c4d5f6g'
|
||||
stconf_service_creationclass = 'Clar_StorageConfigurationService'
|
||||
ctrlconf_service_creationclass = 'Clar_ControllerConfigurationService'
|
||||
rep_service_creationclass = 'Clar_ReplicationService'
|
||||
vol_creationclass = 'Clar_StorageVolume'
|
||||
pool_creationclass = 'Clar_UnifiedStoragePool'
|
||||
lunmask_creationclass = 'Clar_LunMaskingSCSIProtocolController'
|
||||
unit_creationclass = 'CIM_ProtocolControllerForUnit'
|
||||
storage_type = 'gold'
|
||||
|
||||
test_volume = {'name': 'vol1',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '1',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'display_description': 'test volume',
|
||||
'volume_type_id': None}
|
||||
test_failed_volume = {'name': 'failed_vol',
|
||||
'size': 1,
|
||||
'volume_name': 'failed_vol',
|
||||
'id': '4',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'failed_vol',
|
||||
'display_description': 'test failed volume',
|
||||
'volume_type_id': None}
|
||||
test_snapshot = {'name': 'snapshot1',
|
||||
'size': 1,
|
||||
'id': '4444',
|
||||
'volume_name': 'vol1',
|
||||
'volume_size': 1,
|
||||
'project_id': 'project'}
|
||||
test_clone = {'name': 'clone1',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '2',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'clone1',
|
||||
'display_description': 'volume created from snapshot',
|
||||
'volume_type_id': None}
|
||||
test_clone3 = {'name': 'clone3',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '3',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'clone3',
|
||||
'display_description': 'cloned volume',
|
||||
'volume_type_id': None}
|
||||
test_snapshot_vmax = {'name': 'snapshot_vmax',
|
||||
'size': 1,
|
||||
'id': '4445',
|
||||
'volume_name': 'vol1',
|
||||
'volume_size': 1,
|
||||
'project_id': 'project'}
|
||||
failed_snapshot_replica = {'name': 'failed_snapshot_replica',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '5',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'display_description': 'failed snapshot replica',
|
||||
'volume_type_id': None}
|
||||
failed_snapshot_sync = {'name': 'failed_snapshot_sync',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '6',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'failed_snapshot_sync',
|
||||
'display_description': 'failed snapshot sync',
|
||||
'volume_type_id': None}
|
||||
failed_clone_replica = {'name': 'failed_clone_replica',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '7',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'display_description': 'failed clone replica',
|
||||
'volume_type_id': None}
|
||||
failed_clone_sync = {'name': 'failed_clone_sync',
|
||||
'size': 1,
|
||||
'volume_name': 'vol1',
|
||||
'id': '8',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'display_description': 'failed clone sync',
|
||||
'volume_type_id': None}
|
||||
failed_delete_vol = {'name': 'failed_delete_vol',
|
||||
'size': 1,
|
||||
'volume_name': 'failed_delete_vol',
|
||||
'id': '99999',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'failed delete vol',
|
||||
'display_description': 'failed delete volume',
|
||||
'volume_type_id': None}
|
||||
|
||||
|
||||
class EMC_StorageVolume(dict):
|
||||
pass
|
||||
|
||||
|
||||
class SE_ConcreteJob(dict):
|
||||
pass
|
||||
|
||||
|
||||
class FakeEcomConnection():
|
||||
|
||||
def InvokeMethod(self, MethodName, Service, ElementName=None, InPool=None,
|
||||
ElementType=None, Size=None,
|
||||
SyncType=None, SourceElement=None,
|
||||
Operation=None, Synchronization=None,
|
||||
TheElements=None,
|
||||
LUNames=None, InitiatorPortIDs=None, DeviceAccesses=None,
|
||||
ProtocolControllers=None,
|
||||
MaskingGroup=None, Members=None):
|
||||
|
||||
rc = 0L
|
||||
myjob = SE_ConcreteJob()
|
||||
myjob.classname = 'SE_ConcreteJob'
|
||||
myjob['InstanceID'] = '9999'
|
||||
myjob['status'] = 'success'
|
||||
if ElementName == 'failed_vol' and \
|
||||
MethodName == 'CreateOrModifyElementFromStoragePool':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
elif ElementName == 'failed_snapshot_replica' and \
|
||||
MethodName == 'CreateElementReplica':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
elif Synchronization and \
|
||||
Synchronization['SyncedElement']['ElementName'] \
|
||||
== 'failed_snapshot_sync' and \
|
||||
MethodName == 'ModifyReplicaSynchronization':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
elif ElementName == 'failed_clone_replica' and \
|
||||
MethodName == 'CreateElementReplica':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
elif Synchronization and \
|
||||
Synchronization['SyncedElement']['ElementName'] \
|
||||
== 'failed_clone_sync' and \
|
||||
MethodName == 'ModifyReplicaSynchronization':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
elif TheElements and \
|
||||
TheElements[0]['DeviceID'] == '99999' and \
|
||||
MethodName == 'EMCReturnToStoragePool':
|
||||
rc = 10L
|
||||
myjob['status'] = 'failure'
|
||||
|
||||
job = {'Job': myjob}
|
||||
return rc, job
|
||||
|
||||
def EnumerateInstanceNames(self, name):
|
||||
result = None
|
||||
if name == 'EMC_ReplicationService':
|
||||
result = self._enum_replicationservices()
|
||||
elif name == 'EMC_StorageConfigurationService':
|
||||
result = self._enum_stconfsvcs()
|
||||
elif name == 'EMC_ControllerConfigurationService':
|
||||
result = self._enum_ctrlconfsvcs()
|
||||
elif name == 'EMC_VirtualProvisioningPool':
|
||||
result = self._enum_pools()
|
||||
elif name == 'EMC_UnifiedStoragePool':
|
||||
result = self._enum_pools()
|
||||
elif name == 'EMC_StorageVolume':
|
||||
result = self._enum_storagevolumes()
|
||||
elif name == 'Clar_StorageVolume':
|
||||
result = self._enum_storagevolumes()
|
||||
elif name == 'SE_StorageSynchronized_SV_SV':
|
||||
result = self._enum_syncsvsvs()
|
||||
elif name == 'CIM_ProtocolControllerForUnit':
|
||||
result = self._enum_unitnames()
|
||||
elif name == 'EMC_LunMaskingSCSIProtocolController':
|
||||
result = self._enum_lunmaskctrls()
|
||||
elif name == 'EMC_StorageProcessorSystem':
|
||||
result = self._enum_processors()
|
||||
else:
|
||||
result = self._default_enum()
|
||||
return result
|
||||
|
||||
def EnumerateInstances(self, name):
|
||||
result = None
|
||||
if name == 'EMC_VirtualProvisioningPool':
|
||||
result = self._enum_pool_details()
|
||||
elif name == 'EMC_UnifiedStoragePool':
|
||||
result = self._enum_pool_details()
|
||||
else:
|
||||
result = self._default_enum()
|
||||
return result
|
||||
|
||||
def GetInstance(self, objectpath, LocalOnly=False):
|
||||
try:
|
||||
name = objectpath['CreationClassName']
|
||||
except KeyError:
|
||||
name = objectpath.classname
|
||||
result = None
|
||||
if name == 'Clar_StorageVolume':
|
||||
result = self._getinstance_storagevolume(objectpath)
|
||||
elif name == 'CIM_ProtocolControllerForUnit':
|
||||
result = self._getinstance_unit(objectpath)
|
||||
elif name == 'Clar_LunMaskingSCSIProtocolController':
|
||||
result = self._getinstance_lunmask()
|
||||
elif name == 'SE_ConcreteJob':
|
||||
result = self._getinstance_job(objectpath)
|
||||
elif name == 'SE_StorageSynchronized_SV_SV':
|
||||
result = self._getinstance_syncsvsv(objectpath)
|
||||
else:
|
||||
result = self._default_getinstance(objectpath)
|
||||
return result
|
||||
|
||||
def Associators(self, objectpath, resultClass='EMC_StorageHardwareID'):
|
||||
result = None
|
||||
if resultClass == 'EMC_StorageHardwareID':
|
||||
result = self._assoc_hdwid()
|
||||
elif resultClass == 'EMC_iSCSIProtocolEndpoint':
|
||||
result = self._assoc_endpoint()
|
||||
else:
|
||||
result = self._default_assoc(objectpath)
|
||||
return result
|
||||
|
||||
def AssociatorNames(self, objectpath,
|
||||
resultClass='EMC_LunMaskingSCSIProtocolController'):
|
||||
result = None
|
||||
if resultClass == 'EMC_LunMaskingSCSIProtocolController':
|
||||
result = self._assocnames_lunmaskctrl()
|
||||
else:
|
||||
result = self._default_assocnames(objectpath)
|
||||
return result
|
||||
|
||||
def ReferenceNames(self, objectpath,
|
||||
ResultClass='CIM_ProtocolControllerForUnit'):
|
||||
result = None
|
||||
if ResultClass == 'CIM_ProtocolControllerForUnit':
|
||||
result = self._ref_unitnames()
|
||||
else:
|
||||
result = self._default_ref(objectpath)
|
||||
return result
|
||||
|
||||
def _ref_unitnames(self):
|
||||
units = []
|
||||
unit = {}
|
||||
|
||||
dependent = {}
|
||||
dependent['CreationClassName'] = vol_creationclass
|
||||
dependent['DeviceID'] = test_volume['id']
|
||||
dependent['ElementName'] = test_volume['name']
|
||||
dependent['SystemName'] = storage_system
|
||||
|
||||
antecedent = {}
|
||||
antecedent['CreationClassName'] = lunmask_creationclass
|
||||
antecedent['DeviceID'] = lunmaskctrl_id
|
||||
antecedent['SystemName'] = storage_system
|
||||
|
||||
unit['Dependent'] = dependent
|
||||
unit['Antecedent'] = antecedent
|
||||
unit['CreationClassName'] = unit_creationclass
|
||||
units.append(unit)
|
||||
|
||||
return units
|
||||
|
||||
def _default_ref(self, objectpath):
|
||||
return objectpath
|
||||
|
||||
def _assoc_hdwid(self):
|
||||
assocs = []
|
||||
assoc = {}
|
||||
assoc['StorageID'] = initiator1
|
||||
assocs.append(assoc)
|
||||
return assocs
|
||||
|
||||
def _assoc_endpoint(self):
|
||||
assocs = []
|
||||
assoc = {}
|
||||
assoc['Name'] = 'iqn.1992-04.com.emc:cx.apm00123907237.a8,t,0x0001'
|
||||
assoc['SystemName'] = storage_system + '+SP_A+8'
|
||||
assocs.append(assoc)
|
||||
return assocs
|
||||
|
||||
def _default_assoc(self, objectpath):
|
||||
return objectpath
|
||||
|
||||
def _assocnames_lunmaskctrl(self):
|
||||
return self._enum_lunmaskctrls()
|
||||
|
||||
def _default_assocnames(self, objectpath):
|
||||
return objectpath
|
||||
|
||||
def _getinstance_storagevolume(self, objectpath):
|
||||
instance = EMC_StorageVolume()
|
||||
vols = self._enum_storagevolumes()
|
||||
for vol in vols:
|
||||
if vol['DeviceID'] == objectpath['DeviceID']:
|
||||
instance = vol
|
||||
break
|
||||
return instance
|
||||
|
||||
def _getinstance_syncsvsv(self, objectpath):
|
||||
foundsync = None
|
||||
syncs = self._enum_syncsvsvs()
|
||||
for sync in syncs:
|
||||
if (sync['SyncedElement'] == objectpath['SyncedElement'] and
|
||||
sync['SystemElement'] == objectpath['SystemElement']):
|
||||
foundsync = sync
|
||||
break
|
||||
return foundsync
|
||||
|
||||
def _getinstance_lunmask(self):
|
||||
lunmask = {}
|
||||
lunmask['CreationClassName'] = lunmask_creationclass
|
||||
lunmask['DeviceID'] = lunmaskctrl_id
|
||||
lunmask['SystemName'] = storage_system
|
||||
return lunmask
|
||||
|
||||
def _getinstance_unit(self, objectpath):
|
||||
unit = {}
|
||||
|
||||
dependent = {}
|
||||
dependent['CreationClassName'] = vol_creationclass
|
||||
dependent['DeviceID'] = test_volume['id']
|
||||
dependent['ElementName'] = test_volume['name']
|
||||
dependent['SystemName'] = storage_system
|
||||
|
||||
antecedent = {}
|
||||
antecedent['CreationClassName'] = lunmask_creationclass
|
||||
antecedent['DeviceID'] = lunmaskctrl_id
|
||||
antecedent['SystemName'] = storage_system
|
||||
|
||||
unit['Dependent'] = dependent
|
||||
unit['Antecedent'] = antecedent
|
||||
unit['CreationClassName'] = unit_creationclass
|
||||
unit['DeviceNumber'] = '0'
|
||||
|
||||
return unit
|
||||
|
||||
def _getinstance_job(self, jobpath):
|
||||
jobinstance = {}
|
||||
jobinstance['InstanceID'] = '9999'
|
||||
if jobpath['status'] == 'failure':
|
||||
jobinstance['JobState'] = 10
|
||||
jobinstance['ErrorCode'] = 99
|
||||
jobinstance['ErrorDescription'] = 'Failure'
|
||||
else:
|
||||
jobinstance['JobState'] = 7
|
||||
jobinstance['ErrorCode'] = 0
|
||||
jobinstance['ErrorDescription'] = ''
|
||||
return jobinstance
|
||||
|
||||
def _default_getinstance(self, objectpath):
|
||||
return objectpath
|
||||
|
||||
def _enum_replicationservices(self):
|
||||
rep_services = []
|
||||
rep_service = {}
|
||||
rep_service['SystemName'] = storage_system
|
||||
rep_service['CreationClassName'] = rep_service_creationclass
|
||||
rep_services.append(rep_service)
|
||||
return rep_services
|
||||
|
||||
def _enum_stconfsvcs(self):
|
||||
conf_services = []
|
||||
conf_service = {}
|
||||
conf_service['SystemName'] = storage_system
|
||||
conf_service['CreationClassName'] = stconf_service_creationclass
|
||||
conf_services.append(conf_service)
|
||||
return conf_services
|
||||
|
||||
def _enum_ctrlconfsvcs(self):
|
||||
conf_services = []
|
||||
conf_service = {}
|
||||
conf_service['SystemName'] = storage_system
|
||||
conf_service['CreationClassName'] = ctrlconf_service_creationclass
|
||||
conf_services.append(conf_service)
|
||||
return conf_services
|
||||
|
||||
def _enum_pools(self):
|
||||
pools = []
|
||||
pool = {}
|
||||
pool['InstanceID'] = storage_system + '+U+' + storage_type
|
||||
pool['CreationClassName'] = 'Clar_UnifiedStoragePool'
|
||||
pools.append(pool)
|
||||
return pools
|
||||
|
||||
def _enum_pool_details(self):
|
||||
pools = []
|
||||
pool = {}
|
||||
pool['InstanceID'] = storage_system + '+U+' + storage_type
|
||||
pool['CreationClassName'] = 'Clar_UnifiedStoragePool'
|
||||
pool['TotalManagedSpace'] = 12345678
|
||||
pool['RemainingManagedSpace'] = 123456
|
||||
pools.append(pool)
|
||||
return pools
|
||||
|
||||
def _enum_storagevolumes(self):
|
||||
vols = []
|
||||
vol = EMC_StorageVolume()
|
||||
vol['CreationClassName'] = 'Clar_StorageVolume'
|
||||
vol['ElementName'] = test_volume['name']
|
||||
vol['DeviceID'] = test_volume['id']
|
||||
vol['SystemName'] = storage_system
|
||||
vol.path = {'DeviceID': vol['DeviceID']}
|
||||
vols.append(vol)
|
||||
|
||||
snap_vol = EMC_StorageVolume()
|
||||
snap_vol['CreationClassName'] = 'Clar_StorageVolume'
|
||||
snap_vol['ElementName'] = test_snapshot['name']
|
||||
snap_vol['DeviceID'] = test_snapshot['id']
|
||||
snap_vol['SystemName'] = storage_system
|
||||
snap_vol.path = {'DeviceID': snap_vol['DeviceID']}
|
||||
vols.append(snap_vol)
|
||||
|
||||
clone_vol = EMC_StorageVolume()
|
||||
clone_vol['CreationClassName'] = 'Clar_StorageVolume'
|
||||
clone_vol['ElementName'] = test_clone['name']
|
||||
clone_vol['DeviceID'] = test_clone['id']
|
||||
clone_vol['SystemName'] = storage_system
|
||||
clone_vol.path = {'DeviceID': clone_vol['DeviceID']}
|
||||
vols.append(clone_vol)
|
||||
|
||||
clone_vol3 = EMC_StorageVolume()
|
||||
clone_vol3['CreationClassName'] = 'Clar_StorageVolume'
|
||||
clone_vol3['ElementName'] = test_clone3['name']
|
||||
clone_vol3['DeviceID'] = test_clone3['id']
|
||||
clone_vol3['SystemName'] = storage_system
|
||||
clone_vol3.path = {'DeviceID': clone_vol3['DeviceID']}
|
||||
vols.append(clone_vol3)
|
||||
|
||||
snap_vol_vmax = EMC_StorageVolume()
|
||||
snap_vol_vmax['CreationClassName'] = 'Symm_StorageVolume'
|
||||
snap_vol_vmax['ElementName'] = test_snapshot_vmax['name']
|
||||
snap_vol_vmax['DeviceID'] = test_snapshot_vmax['id']
|
||||
snap_vol_vmax['SystemName'] = storage_system_vmax
|
||||
snap_vol_vmax.path = {'DeviceID': snap_vol_vmax['DeviceID']}
|
||||
vols.append(snap_vol_vmax)
|
||||
|
||||
failed_snap_replica = EMC_StorageVolume()
|
||||
failed_snap_replica['CreationClassName'] = 'Clar_StorageVolume'
|
||||
failed_snap_replica['ElementName'] = failed_snapshot_replica['name']
|
||||
failed_snap_replica['DeviceID'] = failed_snapshot_replica['id']
|
||||
failed_snap_replica['SystemName'] = storage_system
|
||||
failed_snap_replica.path = {
|
||||
'DeviceID': failed_snap_replica['DeviceID']}
|
||||
vols.append(failed_snap_replica)
|
||||
|
||||
failed_snap_sync = EMC_StorageVolume()
|
||||
failed_snap_sync['CreationClassName'] = 'Clar_StorageVolume'
|
||||
failed_snap_sync['ElementName'] = failed_snapshot_sync['name']
|
||||
failed_snap_sync['DeviceID'] = failed_snapshot_sync['id']
|
||||
failed_snap_sync['SystemName'] = storage_system
|
||||
failed_snap_sync.path = {
|
||||
'DeviceID': failed_snap_sync['DeviceID']}
|
||||
vols.append(failed_snap_sync)
|
||||
|
||||
failed_clone_rep = EMC_StorageVolume()
|
||||
failed_clone_rep['CreationClassName'] = 'Clar_StorageVolume'
|
||||
failed_clone_rep['ElementName'] = failed_clone_replica['name']
|
||||
failed_clone_rep['DeviceID'] = failed_clone_replica['id']
|
||||
failed_clone_rep['SystemName'] = storage_system
|
||||
failed_clone_rep.path = {
|
||||
'DeviceID': failed_clone_rep['DeviceID']}
|
||||
vols.append(failed_clone_rep)
|
||||
|
||||
failed_clone_s = EMC_StorageVolume()
|
||||
failed_clone_s['CreationClassName'] = 'Clar_StorageVolume'
|
||||
failed_clone_s['ElementName'] = failed_clone_sync['name']
|
||||
failed_clone_s['DeviceID'] = failed_clone_sync['id']
|
||||
failed_clone_s['SystemName'] = storage_system
|
||||
failed_clone_s.path = {
|
||||
'DeviceID': failed_clone_s['DeviceID']}
|
||||
vols.append(failed_clone_s)
|
||||
|
||||
failed_delete_vol = EMC_StorageVolume()
|
||||
failed_delete_vol['CreationClassName'] = 'Clar_StorageVolume'
|
||||
failed_delete_vol['ElementName'] = 'failed_delete_vol'
|
||||
failed_delete_vol['DeviceID'] = '99999'
|
||||
failed_delete_vol['SystemName'] = storage_system
|
||||
failed_delete_vol.path = {'DeviceID': failed_delete_vol['DeviceID']}
|
||||
vols.append(failed_delete_vol)
|
||||
|
||||
return vols
|
||||
|
||||
def _enum_syncsvsvs(self):
|
||||
syncs = []
|
||||
|
||||
vols = self._enum_storagevolumes()
|
||||
|
||||
sync = self._create_sync(vols[0], vols[1], 100)
|
||||
syncs.append(sync)
|
||||
|
||||
sync2 = self._create_sync(vols[1], vols[2], 100)
|
||||
syncs.append(sync2)
|
||||
|
||||
sync3 = self._create_sync(vols[0], vols[3], 100)
|
||||
syncs.append(sync3)
|
||||
|
||||
objpath1 = vols[1]
|
||||
for vol in vols:
|
||||
if vol['ElementName'] == 'failed_snapshot_sync':
|
||||
objpath2 = vol
|
||||
break
|
||||
sync4 = self._create_sync(objpath1, objpath2, 100)
|
||||
syncs.append(sync4)
|
||||
|
||||
objpath1 = vols[0]
|
||||
for vol in vols:
|
||||
if vol['ElementName'] == 'failed_clone_sync':
|
||||
objpath2 = vol
|
||||
break
|
||||
sync5 = self._create_sync(objpath1, objpath2, 100)
|
||||
syncs.append(sync5)
|
||||
|
||||
return syncs
|
||||
|
||||
def _create_sync(self, objpath1, objpath2, percentsynced):
|
||||
sync = {}
|
||||
sync['SyncedElement'] = objpath2
|
||||
sync['SystemElement'] = objpath1
|
||||
sync['CreationClassName'] = 'SE_StorageSynchronized_SV_SV'
|
||||
sync['PercentSynced'] = percentsynced
|
||||
return sync
|
||||
|
||||
def _enum_unitnames(self):
|
||||
return self._ref_unitnames()
|
||||
|
||||
def _enum_lunmaskctrls(self):
|
||||
ctrls = []
|
||||
ctrl = {}
|
||||
ctrl['CreationClassName'] = lunmask_creationclass
|
||||
ctrl['DeviceID'] = lunmaskctrl_id
|
||||
ctrl['SystemName'] = storage_system
|
||||
ctrls.append(ctrl)
|
||||
return ctrls
|
||||
|
||||
def _enum_processors(self):
|
||||
ctrls = []
|
||||
ctrl = {}
|
||||
ctrl['CreationClassName'] = 'Clar_StorageProcessorSystem'
|
||||
ctrl['Name'] = storage_system + '+SP_A'
|
||||
ctrls.append(ctrl)
|
||||
return ctrls
|
||||
|
||||
def _default_enum(self):
|
||||
names = []
|
||||
name = {}
|
||||
name['Name'] = 'default'
|
||||
names.append(name)
|
||||
return names
|
||||
|
||||
|
||||
class EMCSMISISCSIDriverTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
super(EMCSMISISCSIDriverTestCase, self).setUp()
|
||||
self.config_file_path = None
|
||||
self.create_fake_config_file()
|
||||
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.manila_emc_config_file = self.config_file_path
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
self.stubs.Set(EMCSMISISCSIDriver, '_do_iscsi_discovery',
|
||||
self.fake_do_iscsi_discovery)
|
||||
self.stubs.Set(EMCSMISCommon, '_get_ecom_connection',
|
||||
self.fake_ecom_connection)
|
||||
driver = EMCSMISISCSIDriver(configuration=configuration)
|
||||
self.driver = driver
|
||||
|
||||
def create_fake_config_file(self):
|
||||
doc = Document()
|
||||
emc = doc.createElement("EMC")
|
||||
doc.appendChild(emc)
|
||||
|
||||
storagetype = doc.createElement("StorageType")
|
||||
storagetypetext = doc.createTextNode("gold")
|
||||
emc.appendChild(storagetype)
|
||||
storagetype.appendChild(storagetypetext)
|
||||
|
||||
ecomserverip = doc.createElement("EcomServerIp")
|
||||
ecomserveriptext = doc.createTextNode("1.1.1.1")
|
||||
emc.appendChild(ecomserverip)
|
||||
ecomserverip.appendChild(ecomserveriptext)
|
||||
|
||||
ecomserverport = doc.createElement("EcomServerPort")
|
||||
ecomserverporttext = doc.createTextNode("10")
|
||||
emc.appendChild(ecomserverport)
|
||||
ecomserverport.appendChild(ecomserverporttext)
|
||||
|
||||
ecomusername = doc.createElement("EcomUserName")
|
||||
ecomusernametext = doc.createTextNode("user")
|
||||
emc.appendChild(ecomusername)
|
||||
ecomusername.appendChild(ecomusernametext)
|
||||
|
||||
ecompassword = doc.createElement("EcomPassword")
|
||||
ecompasswordtext = doc.createTextNode("pass")
|
||||
emc.appendChild(ecompassword)
|
||||
ecompassword.appendChild(ecompasswordtext)
|
||||
|
||||
self.config_file_path = self.tempdir + '/' + config_file_name
|
||||
f = open(self.config_file_path, 'w')
|
||||
doc.writexml(f)
|
||||
f.close()
|
||||
|
||||
def fake_ecom_connection(self):
|
||||
conn = FakeEcomConnection()
|
||||
return conn
|
||||
|
||||
def fake_do_iscsi_discovery(self, volume):
|
||||
output = []
|
||||
item = '10.0.0.3:3260,1 iqn.1992-04.com.emc:cx.apm00123907237.a8'
|
||||
item2 = '10.0.0.4:3260,2 iqn.1992-04.com.emc:cx.apm00123907237.b8'
|
||||
output.append(item)
|
||||
output.append(item2)
|
||||
return output
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
self.driver.get_volume_stats(True)
|
||||
|
||||
def test_create_destroy(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_snapshot_destroy(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.driver.create_snapshot(test_snapshot)
|
||||
self.driver.create_volume_from_snapshot(
|
||||
test_clone, test_snapshot)
|
||||
self.driver.create_cloned_volume(
|
||||
test_clone3, test_volume)
|
||||
self.driver.delete_volume(test_clone)
|
||||
self.driver.delete_volume(test_clone3)
|
||||
self.driver.delete_snapshot(test_snapshot)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_map_unmap(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
export = self.driver.create_export(None, test_volume)
|
||||
test_volume['provider_location'] = export['provider_location']
|
||||
test_volume['EMCCurrentOwningStorageProcessor'] = 'SP_A'
|
||||
connector = {'initiator': initiator1}
|
||||
connection_info = self.driver.initialize_connection(test_volume,
|
||||
connector)
|
||||
self.driver.terminate_connection(test_volume, connector)
|
||||
self.driver.remove_export(None, test_volume)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume,
|
||||
test_failed_volume)
|
||||
|
||||
def test_create_volume_snapshot_unsupported(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.driver.create_snapshot(test_snapshot_vmax)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
test_clone,
|
||||
test_snapshot_vmax)
|
||||
self.driver.delete_snapshot(test_snapshot_vmax)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_snapshot_replica_failed(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.driver.create_snapshot(test_snapshot)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
failed_snapshot_replica,
|
||||
test_snapshot)
|
||||
self.driver.delete_snapshot(test_snapshot)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_snapshot_sync_failed(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.driver.create_snapshot(test_snapshot)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
failed_snapshot_sync,
|
||||
test_snapshot)
|
||||
self.driver.delete_snapshot(test_snapshot)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_clone_replica_failed(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_cloned_volume,
|
||||
failed_clone_replica,
|
||||
test_volume)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_create_volume_clone_sync_failed(self):
|
||||
self.driver.create_volume(test_volume)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_cloned_volume,
|
||||
failed_clone_sync,
|
||||
test_volume)
|
||||
self.driver.delete_volume(test_volume)
|
||||
|
||||
def test_delete_volume_notfound(self):
|
||||
notfound_delete_vol = {}
|
||||
notfound_delete_vol['name'] = 'notfound_delete_vol'
|
||||
notfound_delete_vol['id'] = '10'
|
||||
self.driver.delete_volume(notfound_delete_vol)
|
||||
|
||||
def test_delete_volume_failed(self):
|
||||
self.driver.create_volume(failed_delete_vol)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.delete_volume,
|
||||
failed_delete_vol)
|
||||
|
||||
def _cleanup(self):
|
||||
bExists = os.path.exists(self.config_file_path)
|
||||
if bExists:
|
||||
os.remove(self.config_file_path)
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
def tearDown(self):
|
||||
self._cleanup()
|
||||
super(EMCSMISISCSIDriverTestCase, self).tearDown()
|
|
@ -1,579 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2013 Red Hat, Inc.
|
||||
# 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.
|
||||
"""Unit tests for the GlusterFS driver module."""
|
||||
|
||||
import __builtin__
|
||||
import errno
|
||||
import os
|
||||
|
||||
import mox as mox_lib
|
||||
from mox import IgnoreArg
|
||||
from mox import IsA
|
||||
from mox import stubout
|
||||
|
||||
from manila import context
|
||||
from manila import exception
|
||||
from manila.exception import ProcessExecutionError
|
||||
from manila import test
|
||||
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers import glusterfs
|
||||
|
||||
|
||||
class DumbVolume(object):
|
||||
fields = {}
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.fields[key] = value
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.fields[item]
|
||||
|
||||
|
||||
class GlusterFsDriverTestCase(test.TestCase):
|
||||
"""Test case for GlusterFS driver."""
|
||||
|
||||
TEST_EXPORT1 = 'glusterfs-host1:/export'
|
||||
TEST_EXPORT2 = 'glusterfs-host2:/export'
|
||||
TEST_SIZE_IN_GB = 1
|
||||
TEST_MNT_POINT = '/mnt/glusterfs'
|
||||
TEST_MNT_POINT_BASE = '/mnt/test'
|
||||
TEST_LOCAL_PATH = '/mnt/glusterfs/volume-123'
|
||||
TEST_FILE_NAME = 'test.txt'
|
||||
TEST_SHARES_CONFIG_FILE = '/etc/manila/test-shares.conf'
|
||||
ONE_GB_IN_BYTES = 1024 * 1024 * 1024
|
||||
|
||||
def setUp(self):
|
||||
self._mox = mox_lib.Mox()
|
||||
self._configuration = mox_lib.MockObject(conf.Configuration)
|
||||
self._configuration.append_config_values(mox_lib.IgnoreArg())
|
||||
self._configuration.glusterfs_shares_config = \
|
||||
self.TEST_SHARES_CONFIG_FILE
|
||||
self._configuration.glusterfs_mount_point_base = \
|
||||
self.TEST_MNT_POINT_BASE
|
||||
self._configuration.glusterfs_disk_util = 'df'
|
||||
self._configuration.glusterfs_sparsed_volumes = True
|
||||
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self._driver = glusterfs.GlusterfsDriver(
|
||||
configuration=self._configuration)
|
||||
|
||||
def tearDown(self):
|
||||
self._mox.UnsetStubs()
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def stub_out_not_replaying(self, obj, attr_name):
|
||||
attr_to_replace = getattr(obj, attr_name)
|
||||
stub = mox_lib.MockObject(attr_to_replace)
|
||||
self.stubs.Set(obj, attr_name, stub)
|
||||
|
||||
def test_local_path(self):
|
||||
"""local_path common use case."""
|
||||
glusterfs.FLAGS.glusterfs_mount_point_base = self.TEST_MNT_POINT_BASE
|
||||
drv = self._driver
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['provider_location'] = self.TEST_EXPORT1
|
||||
volume['name'] = 'volume-123'
|
||||
|
||||
self.assertEqual(
|
||||
'/mnt/test/ab03ab34eaca46a5fb81878f7e9b91fc/volume-123',
|
||||
drv.local_path(volume))
|
||||
|
||||
def test_mount_glusterfs_should_mount_correctly(self):
|
||||
"""_mount_glusterfs common case usage."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute('mount', '-t', 'glusterfs', self.TEST_EXPORT1,
|
||||
self.TEST_MNT_POINT, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_glusterfs(self.TEST_EXPORT1, self.TEST_MNT_POINT)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_glusterfs_should_suppress_already_mounted_error(self):
|
||||
"""_mount_glusterfs should suppress already mounted error if
|
||||
ensure=True
|
||||
"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute('mount', '-t', 'glusterfs', self.TEST_EXPORT1,
|
||||
self.TEST_MNT_POINT, run_as_root=True).\
|
||||
AndRaise(ProcessExecutionError(
|
||||
stderr='is busy or already mounted'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_glusterfs(self.TEST_EXPORT1, self.TEST_MNT_POINT,
|
||||
ensure=True)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_glusterfs_should_reraise_already_mounted_error(self):
|
||||
"""_mount_glusterfs should not suppress already mounted error
|
||||
if ensure=False
|
||||
"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute(
|
||||
'mount',
|
||||
'-t',
|
||||
'glusterfs',
|
||||
self.TEST_EXPORT1,
|
||||
self.TEST_MNT_POINT,
|
||||
run_as_root=True). \
|
||||
AndRaise(ProcessExecutionError(stderr='is busy or '
|
||||
'already mounted'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(ProcessExecutionError, drv._mount_glusterfs,
|
||||
self.TEST_EXPORT1, self.TEST_MNT_POINT,
|
||||
ensure=False)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_glusterfs_should_create_mountpoint_if_not_yet(self):
|
||||
"""_mount_glusterfs should create mountpoint if it doesn't exist."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_glusterfs(self.TEST_EXPORT1, self.TEST_MNT_POINT)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_get_hash_str(self):
|
||||
"""_get_hash_str should calculation correct value."""
|
||||
drv = self._driver
|
||||
|
||||
self.assertEqual('ab03ab34eaca46a5fb81878f7e9b91fc',
|
||||
drv._get_hash_str(self.TEST_EXPORT1))
|
||||
|
||||
def test_get_mount_point_for_share(self):
|
||||
"""_get_mount_point_for_share should calculate correct value."""
|
||||
drv = self._driver
|
||||
|
||||
glusterfs.FLAGS.glusterfs_mount_point_base = self.TEST_MNT_POINT_BASE
|
||||
|
||||
self.assertEqual('/mnt/test/ab03ab34eaca46a5fb81878f7e9b91fc',
|
||||
drv._get_mount_point_for_share(
|
||||
self.TEST_EXPORT1))
|
||||
|
||||
def test_get_available_capacity_with_df(self):
|
||||
"""_get_available_capacity should calculate correct value."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
df_total_size = 2620544
|
||||
df_avail = 1490560
|
||||
df_head = 'Filesystem 1K-blocks Used Available Use% Mounted on\n'
|
||||
df_data = 'glusterfs-host:/export %d 996864 %d 41%% /mnt' % \
|
||||
(df_total_size, df_avail)
|
||||
df_output = df_head + df_data
|
||||
|
||||
setattr(glusterfs.FLAGS, 'glusterfs_disk_util', 'df')
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('df', '--portability', '--block-size', '1',
|
||||
self.TEST_MNT_POINT,
|
||||
run_as_root=True).AndReturn((df_output, None))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEquals((df_avail, df_total_size),
|
||||
drv._get_available_capacity(
|
||||
self.TEST_EXPORT1))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
delattr(glusterfs.FLAGS, 'glusterfs_disk_util')
|
||||
|
||||
def test_get_available_capacity_with_du(self):
|
||||
"""_get_available_capacity should calculate correct value."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
old_value = self._configuration.glusterfs_disk_util
|
||||
self._configuration.glusterfs_disk_util = 'du'
|
||||
|
||||
df_total_size = 2620544
|
||||
df_used_size = 996864
|
||||
df_avail_size = 1490560
|
||||
df_title = 'Filesystem 1-blocks Used Available Use% Mounted on\n'
|
||||
df_mnt_data = 'glusterfs-host:/export %d %d %d 41%% /mnt' % \
|
||||
(df_total_size,
|
||||
df_used_size,
|
||||
df_avail_size)
|
||||
df_output = df_title + df_mnt_data
|
||||
|
||||
du_used = 490560
|
||||
du_output = '%d /mnt' % du_used
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('df', '--portability', '--block-size', '1',
|
||||
self.TEST_MNT_POINT,
|
||||
run_as_root=True).\
|
||||
AndReturn((df_output, None))
|
||||
drv._execute('du', '-sb', '--apparent-size',
|
||||
'--exclude', '*snapshot*',
|
||||
self.TEST_MNT_POINT,
|
||||
run_as_root=True).AndReturn((du_output, None))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEquals((df_total_size - du_used, df_total_size),
|
||||
drv._get_available_capacity(
|
||||
self.TEST_EXPORT1))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
self._configuration.glusterfs_disk_util = old_value
|
||||
|
||||
def test_load_shares_config(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
glusterfs.FLAGS.glusterfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
mox.StubOutWithMock(__builtin__, 'open')
|
||||
config_data = []
|
||||
config_data.append(self.TEST_EXPORT1)
|
||||
config_data.append('#' + self.TEST_EXPORT2)
|
||||
config_data.append('')
|
||||
__builtin__.open(self.TEST_SHARES_CONFIG_FILE).AndReturn(config_data)
|
||||
mox.ReplayAll()
|
||||
|
||||
shares = drv._load_shares_config()
|
||||
|
||||
self.assertEqual([self.TEST_EXPORT1], shares)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_share_mounted(self):
|
||||
"""_ensure_share_mounted simple use case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_mount_glusterfs')
|
||||
drv._mount_glusterfs(self.TEST_EXPORT1, self.TEST_MNT_POINT,
|
||||
ensure=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_share_mounted(self.TEST_EXPORT1)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_shares_mounted_should_save_mounting_successfully(self):
|
||||
"""_ensure_shares_mounted should save share if mounted with success."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_load_shares_config')
|
||||
drv._load_shares_config().AndReturn([self.TEST_EXPORT1])
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
self.assertEqual(1, len(drv._mounted_shares))
|
||||
self.assertEqual(self.TEST_EXPORT1, drv._mounted_shares[0])
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_shares_mounted_should_not_save_mounting_with_error(self):
|
||||
"""_ensure_shares_mounted should not save share if failed to mount."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_load_shares_config')
|
||||
drv._load_shares_config().AndReturn([self.TEST_EXPORT1])
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_EXPORT1).AndRaise(Exception())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
self.assertEqual(0, len(drv._mounted_shares))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_setup_should_throw_error_if_shares_config_not_configured(self):
|
||||
"""do_setup should throw error if shares config is not configured."""
|
||||
drv = self._driver
|
||||
|
||||
glusterfs.FLAGS.glusterfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
self.assertRaises(exception.GlusterfsException,
|
||||
drv.do_setup, IsA(context.RequestContext))
|
||||
|
||||
def test_setup_should_throw_exception_if_client_is_not_installed(self):
|
||||
"""do_setup should throw exception if client is not installed."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
glusterfs.FLAGS.glusterfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
mox.StubOutWithMock(os.path, 'exists')
|
||||
os.path.exists(self.TEST_SHARES_CONFIG_FILE).AndReturn(True)
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mount.glusterfs', check_exit_code=False).\
|
||||
AndRaise(OSError(errno.ENOENT, 'No such file or directory'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exception.GlusterfsException,
|
||||
drv.do_setup, IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_find_share_should_throw_error_if_there_is_no_mounted_shares(self):
|
||||
"""_find_share should throw error if there is no mounted shares."""
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = []
|
||||
|
||||
self.assertRaises(exception.NotFound, drv._find_share,
|
||||
self.TEST_SIZE_IN_GB)
|
||||
|
||||
def test_find_share(self):
|
||||
"""_find_share simple use case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = [self.TEST_EXPORT1, self.TEST_EXPORT2]
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_available_capacity')
|
||||
drv._get_available_capacity(self.TEST_EXPORT1).\
|
||||
AndReturn((2 * self.ONE_GB_IN_BYTES, 5 * self.ONE_GB_IN_BYTES))
|
||||
drv._get_available_capacity(self.TEST_EXPORT2).\
|
||||
AndReturn((3 * self.ONE_GB_IN_BYTES, 10 * self.ONE_GB_IN_BYTES))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEqual(self.TEST_EXPORT2,
|
||||
drv._find_share(self.TEST_SIZE_IN_GB))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_find_share_should_throw_error_if_there_is_no_enough_place(self):
|
||||
"""_find_share should throw error if there is no share to host vol."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = [self.TEST_EXPORT1,
|
||||
self.TEST_EXPORT2]
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_available_capacity')
|
||||
drv._get_available_capacity(self.TEST_EXPORT1).\
|
||||
AndReturn((0, 5 * self.ONE_GB_IN_BYTES))
|
||||
drv._get_available_capacity(self.TEST_EXPORT2).\
|
||||
AndReturn((0, 10 * self.ONE_GB_IN_BYTES))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exception.GlusterfsNoSuitableShareFound,
|
||||
drv._find_share,
|
||||
self.TEST_SIZE_IN_GB)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _simple_volume(self):
|
||||
volume = DumbVolume()
|
||||
volume['provider_location'] = '127.0.0.1:/mnt'
|
||||
volume['name'] = 'volume_name'
|
||||
volume['size'] = 10
|
||||
|
||||
return volume
|
||||
|
||||
def test_create_sparsed_volume(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
volume = self._simple_volume()
|
||||
|
||||
setattr(glusterfs.FLAGS, 'glusterfs_sparsed_volumes', True)
|
||||
|
||||
mox.StubOutWithMock(drv, '_create_sparsed_file')
|
||||
mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
|
||||
|
||||
drv._create_sparsed_file(IgnoreArg(), IgnoreArg())
|
||||
drv._set_rw_permissions_for_all(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._do_create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
delattr(glusterfs.FLAGS, 'glusterfs_sparsed_volumes')
|
||||
|
||||
def test_create_nonsparsed_volume(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
volume = self._simple_volume()
|
||||
|
||||
old_value = self._configuration.glusterfs_sparsed_volumes
|
||||
self._configuration.glusterfs_sparsed_volumes = False
|
||||
|
||||
mox.StubOutWithMock(drv, '_create_regular_file')
|
||||
mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
|
||||
|
||||
drv._create_regular_file(IgnoreArg(), IgnoreArg())
|
||||
drv._set_rw_permissions_for_all(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._do_create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
self._configuration.glusterfs_sparsed_volumes = old_value
|
||||
|
||||
def test_create_volume_should_ensure_glusterfs_mounted(self):
|
||||
"""create_volume ensures shares provided in config are mounted."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(glusterfs, 'LOG')
|
||||
self.stub_out_not_replaying(drv, '_find_share')
|
||||
self.stub_out_not_replaying(drv, '_do_create_volume')
|
||||
|
||||
mox.StubOutWithMock(drv, '_ensure_shares_mounted')
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['size'] = self.TEST_SIZE_IN_GB
|
||||
drv.create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_volume_should_return_provider_location(self):
|
||||
"""create_volume should return provider_location with found share."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(glusterfs, 'LOG')
|
||||
self.stub_out_not_replaying(drv, '_ensure_shares_mounted')
|
||||
self.stub_out_not_replaying(drv, '_do_create_volume')
|
||||
|
||||
mox.StubOutWithMock(drv, '_find_share')
|
||||
drv._find_share(self.TEST_SIZE_IN_GB).AndReturn(self.TEST_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['size'] = self.TEST_SIZE_IN_GB
|
||||
result = drv.create_volume(volume)
|
||||
self.assertEqual(self.TEST_EXPORT1, result['provider_location'])
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_volume(self):
|
||||
"""delete_volume simple test case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_ensure_share_mounted')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = self.TEST_EXPORT1
|
||||
|
||||
mox.StubOutWithMock(drv, 'local_path')
|
||||
drv.local_path(volume).AndReturn(self.TEST_LOCAL_PATH)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('rm', '-f', self.TEST_LOCAL_PATH, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_should_ensure_share_mounted(self):
|
||||
"""delete_volume should ensure that corresponding share is mounted."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_execute')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = self.TEST_EXPORT1
|
||||
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_should_not_delete_if_provider_location_not_provided(self):
|
||||
"""delete_volume shouldn't delete if provider_location missed."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_ensure_share_mounted')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = None
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
File diff suppressed because it is too large
Load Diff
|
@ -1,859 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 Huawei Technologies Co., Ltd.
|
||||
# Copyright (c) 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.
|
||||
"""
|
||||
Tests for HUAWEI volume driver.
|
||||
"""
|
||||
import mox
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from xml.dom.minidom import Document
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.huawei import huawei_iscsi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
LUNInfo = {'ID': None,
|
||||
'Name': None,
|
||||
'Size': None,
|
||||
'LUN WWN': None,
|
||||
'Status': None,
|
||||
'Visible Capacity': None,
|
||||
'Stripe Unit Size': None,
|
||||
'Disk Pool ID': None,
|
||||
'Format Progress': None,
|
||||
'Cache Prefetch Strategy': None,
|
||||
'LUNType': None,
|
||||
'Cache Write Strategy': None,
|
||||
'Running Cache Write Strategy': None,
|
||||
'Consumed Capacity': None,
|
||||
'Pool ID': None,
|
||||
'SnapShot ID': None,
|
||||
'LunCopy ID': None,
|
||||
'Whether Private LUN': None,
|
||||
'Remote Replication ID': None,
|
||||
'Split mirror ID': None,
|
||||
'Owner Controller': None,
|
||||
'Worker Controller': None,
|
||||
'RAID Group ID': None}
|
||||
|
||||
LUNInfoCopy = {'ID': None,
|
||||
'Name': None,
|
||||
'Size': None,
|
||||
'LUN WWN': None,
|
||||
'Status': None,
|
||||
'Visible Capacity': None,
|
||||
'Stripe Unit Size': None,
|
||||
'Disk Pool ID': None,
|
||||
'Format Progress': None,
|
||||
'Cache Prefetch Strategy': None,
|
||||
'LUNType': None,
|
||||
'Cache Write Strategy': None,
|
||||
'Running Cache Write Strategy': None,
|
||||
'Consumed Capacity': None,
|
||||
'Pool ID': None,
|
||||
'SnapShot ID': None,
|
||||
'LunCopy ID': None,
|
||||
'Whether Private LUN': None,
|
||||
'Remote Replication ID': None,
|
||||
'Split mirror ID': None,
|
||||
'Owner Controller': None,
|
||||
'Worker Controller': None,
|
||||
'RAID Group ID': None}
|
||||
|
||||
SnapshotInfo = {'Source LUN ID': None,
|
||||
'Source LUN Name': None,
|
||||
'ID': None,
|
||||
'Name': None,
|
||||
'Type': 'Public',
|
||||
'Status': None,
|
||||
'Time Stamp': '2013-01-15 14:00:00',
|
||||
'Rollback Start Time': '--',
|
||||
'Rollback End Time': '--',
|
||||
'Rollback Speed': '--',
|
||||
'Rollback Progress': '--'}
|
||||
|
||||
MapInfo = {'Host Group ID': None,
|
||||
'Host Group Name': None,
|
||||
'File Engine Cluster': None,
|
||||
'Host ID': None,
|
||||
'Host Name': None,
|
||||
'Os Type': None,
|
||||
'INI Port ID': None,
|
||||
'INI Port Name': None,
|
||||
'INI Port Info': None,
|
||||
'Port Type': None,
|
||||
'Link Status': None,
|
||||
'LUN WWN': None,
|
||||
'DEV LUN ID': None,
|
||||
'Host LUN ID': None}
|
||||
|
||||
HostPort = {'ID': None,
|
||||
'Name': None,
|
||||
'Info': None}
|
||||
|
||||
LUNCopy = {'Name': None,
|
||||
'ID': None,
|
||||
'Type': None,
|
||||
'State': None,
|
||||
'Status': 'Disable'}
|
||||
|
||||
FakeVolume = {'name': 'Volume-lele34fe-223f-dd33-4423-asdfghjklqwe',
|
||||
'size': '2',
|
||||
'id': '0',
|
||||
'wwn': '630303710030303701094b2b00000031',
|
||||
'provider_auth': None}
|
||||
|
||||
FakeVolumeCopy = {'name': 'Volume-jeje34fe-223f-dd33-4423-asdfghjklqwg',
|
||||
'size': '3',
|
||||
'ID': '1',
|
||||
'wwn': '630303710030303701094b2b0000003'}
|
||||
|
||||
FakeLUNCopy = {'ID': '1',
|
||||
'Type': 'FULL',
|
||||
'State': 'Created',
|
||||
'Status': 'Normal'}
|
||||
|
||||
FakeSnapshot = {'name': 'keke34fe-223f-dd33-4423-asdfghjklqwf',
|
||||
'volume_name': 'Volume-lele34fe-223f-dd33-4423-asdfghjklqwe',
|
||||
'id': '3'}
|
||||
|
||||
FakePoolInfo = {'ID': '2',
|
||||
'Level': 'RAID6',
|
||||
'Status': 'Normal',
|
||||
'Free Capacity': '10240',
|
||||
'Disk List': '0,1;0,2;0,3;0,4;0,5;0,6',
|
||||
'Name': 'RAID_001',
|
||||
'Type': 'Thick'}
|
||||
|
||||
FakeConfInfo = {'HostGroup': 'HostGroup_OpenStack',
|
||||
'HostnamePrefix': 'Host_',
|
||||
'DefaultTargetIP': '192.168.100.1',
|
||||
'TargetIQN': 'iqn.2006-08.com.huawei:oceanspace:2103037:',
|
||||
'TargetIQN-T': 'iqn.2006-08.com.huawei:oceanspace:2103037::'
|
||||
'20001:192.168.100.2',
|
||||
'TargetIQN-Dorado5100': 'iqn.2006-08.com.huawei:oceanspace:'
|
||||
'2103037::192.168.100.2',
|
||||
'TargetIQN-Dorado2100G2': 'iqn.2006-08.com.huawei:oceanspace:'
|
||||
'2103037::192.168.100.2-20001',
|
||||
'Initiator Name': 'iqn.1993-08.debian:01:ec2bff7ac3a3',
|
||||
'Initiator TargetIP': '192.168.100.2'}
|
||||
|
||||
FakeConnector = {'initiator': "iqn.1993-08.debian:01:ec2bff7ac3a3"}
|
||||
|
||||
|
||||
class HuaweiVolumeTestCase(test.TestCase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HuaweiVolumeTestCase, self).__init__(*args, **kwargs)
|
||||
|
||||
self.tmp_dir = tempfile.mkdtemp()
|
||||
self.fake_conf_file = self.tmp_dir + '/manila_huawei_conf.xml'
|
||||
self._create_fake_conf_file()
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.manila_huawei_conf_file = self.fake_conf_file
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
self.driver = FakeHuaweiStorage(configuration=configuration)
|
||||
|
||||
self.driver.do_setup({})
|
||||
|
||||
def setUp(self):
|
||||
super(HuaweiVolumeTestCase, self).setUp()
|
||||
self.driver._test_flg = 'check_for_fail'
|
||||
self._test_check_for_setup_errors()
|
||||
|
||||
def test_create_export_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_export,
|
||||
{}, FakeVolume)
|
||||
|
||||
def test_delete_volume_failed(self):
|
||||
self._test_delete_volume()
|
||||
|
||||
def test_create_snapshot_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_snapshot,
|
||||
FakeSnapshot)
|
||||
|
||||
def test_delete_snapshot_failed(self):
|
||||
self._test_delete_snapshot()
|
||||
|
||||
def test_create_luncopy_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
FakeVolumeCopy, FakeSnapshot)
|
||||
|
||||
def test_initialize_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.initialize_connection,
|
||||
FakeVolume, FakeConnector)
|
||||
|
||||
def test_terminate_connection_failed(self):
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.terminate_connection,
|
||||
FakeVolume, FakeConnector)
|
||||
|
||||
def test_normal(self):
|
||||
# test for T Series
|
||||
self.driver._test_flg = 'check_for_T'
|
||||
self._test_check_for_setup_errors()
|
||||
self._test_create_volume()
|
||||
self._test_create_export()
|
||||
self._test_create_snapshot()
|
||||
self._test_create_volume_from_snapshot()
|
||||
self._test_initialize_connection_for_T()
|
||||
self._test_terminate_connection()
|
||||
self._test_delete_snapshot()
|
||||
self._test_delete_volume()
|
||||
self._test_get_get_volume_stats()
|
||||
|
||||
# test for Dorado2100 G2
|
||||
self.driver._test_flg = 'check_for_Dorado2100G2'
|
||||
self._test_check_for_setup_errors()
|
||||
self._test_create_volume()
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_snapshot,
|
||||
FakeSnapshot)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
FakeVolumeCopy, FakeSnapshot)
|
||||
self._test_initialize_connection_for_Dorado2100G2()
|
||||
self._test_terminate_connection()
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.delete_snapshot,
|
||||
FakeSnapshot)
|
||||
self._test_delete_volume()
|
||||
|
||||
# test for Dorado5100
|
||||
self.driver._test_flg = 'check_for_Dorado5100'
|
||||
self._test_check_for_setup_errors()
|
||||
self._test_create_volume()
|
||||
self._test_create_snapshot()
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
FakeVolumeCopy, FakeSnapshot)
|
||||
self._test_initialize_connection_for_Dorado5100()
|
||||
self._test_terminate_connection()
|
||||
self._test_delete_snapshot()
|
||||
self._test_delete_volume()
|
||||
|
||||
def cleanup(self):
|
||||
if os.path.exists(self.fake_conf_file):
|
||||
os.remove(self.fake_conf_file)
|
||||
shutil.rmtree(self.tmp_dir)
|
||||
|
||||
def _create_fake_conf_file(self):
|
||||
doc = Document()
|
||||
|
||||
config = doc.createElement('config')
|
||||
doc.appendChild(config)
|
||||
|
||||
storage = doc.createElement('Storage')
|
||||
config.appendChild(storage)
|
||||
controllerip0 = doc.createElement('ControllerIP0')
|
||||
controllerip0_text = doc.createTextNode('10.10.10.1')
|
||||
controllerip0.appendChild(controllerip0_text)
|
||||
storage.appendChild(controllerip0)
|
||||
controllerip1 = doc.createElement('ControllerIP1')
|
||||
controllerip1_text = doc.createTextNode('10.10.10.2')
|
||||
controllerip1.appendChild(controllerip1_text)
|
||||
storage.appendChild(controllerip1)
|
||||
username = doc.createElement('UserName')
|
||||
username_text = doc.createTextNode('admin')
|
||||
username.appendChild(username_text)
|
||||
storage.appendChild(username)
|
||||
userpassword = doc.createElement('UserPassword')
|
||||
userpassword_text = doc.createTextNode('123456')
|
||||
userpassword.appendChild(userpassword_text)
|
||||
storage.appendChild(userpassword)
|
||||
|
||||
lun = doc.createElement('LUN')
|
||||
config.appendChild(lun)
|
||||
storagepool = doc.createElement('StoragePool')
|
||||
storagepool.setAttribute('Name', 'RAID_001')
|
||||
lun.appendChild(storagepool)
|
||||
storagepool = doc.createElement('StoragePool')
|
||||
storagepool.setAttribute('Name', 'RAID_002')
|
||||
lun.appendChild(storagepool)
|
||||
|
||||
iscsi = doc.createElement('iSCSI')
|
||||
config.appendChild(iscsi)
|
||||
defaulttargetip = doc.createElement('DefaultTargetIP')
|
||||
defaulttargetip_text = doc.createTextNode('192.168.100.1')
|
||||
defaulttargetip.appendChild(defaulttargetip_text)
|
||||
iscsi.appendChild(defaulttargetip)
|
||||
initiator = doc.createElement('Initiator')
|
||||
initiator.setAttribute('Name', 'iqn.1993-08.debian:01:ec2bff7ac3a3')
|
||||
initiator.setAttribute('TargetIP', '192.168.100.2')
|
||||
iscsi.appendChild(initiator)
|
||||
|
||||
file = open(self.fake_conf_file, 'w')
|
||||
file.write(doc.toprettyxml(indent=''))
|
||||
file.close()
|
||||
|
||||
def _test_check_for_setup_errors(self):
|
||||
self.driver.check_for_setup_error()
|
||||
|
||||
def _test_create_volume(self):
|
||||
self.driver.create_volume(FakeVolume)
|
||||
self.assertNotEqual(LUNInfo["ID"], None)
|
||||
self.assertEqual(LUNInfo["RAID Group ID"], FakePoolInfo['ID'])
|
||||
|
||||
def _test_delete_volume(self):
|
||||
self.driver.delete_volume(FakeVolume)
|
||||
self.assertEqual(LUNInfo["ID"], None)
|
||||
|
||||
def _test_create_snapshot(self):
|
||||
self.driver.create_snapshot(FakeSnapshot)
|
||||
self.assertNotEqual(SnapshotInfo["ID"], None)
|
||||
self.assertNotEqual(LUNInfo["ID"], None)
|
||||
self.assertEqual(SnapshotInfo["Status"], 'Active')
|
||||
self.assertEqual(SnapshotInfo["Source LUN ID"], LUNInfo["ID"])
|
||||
|
||||
def _test_delete_snapshot(self):
|
||||
self.driver.delete_snapshot(FakeSnapshot)
|
||||
self.assertEqual(SnapshotInfo["ID"], None)
|
||||
|
||||
def _test_create_volume_from_snapshot(self):
|
||||
self.driver.create_volume_from_snapshot(FakeVolumeCopy, FakeSnapshot)
|
||||
self.assertNotEqual(LUNInfoCopy["ID"], None)
|
||||
|
||||
def _test_create_export(self):
|
||||
retval = self.driver.create_export({}, FakeVolume)
|
||||
self.assertNotEqual(retval, FakeVolume["id"])
|
||||
|
||||
def _test_initialize_connection_for_T(self):
|
||||
connection_data = self.driver.initialize_connection(FakeVolume,
|
||||
FakeConnector)
|
||||
iscsi_properties = connection_data['data']
|
||||
|
||||
self.assertEquals(iscsi_properties['target_iqn'],
|
||||
FakeConfInfo['TargetIQN-T'])
|
||||
self.assertEquals(iscsi_properties['target_portal'],
|
||||
FakeConfInfo['Initiator TargetIP'] + ':3260')
|
||||
self.assertEqual(MapInfo["DEV LUN ID"], FakeVolume['id'])
|
||||
self.assertEqual(MapInfo["INI Port Info"],
|
||||
FakeConnector['initiator'])
|
||||
|
||||
def _test_initialize_connection_for_Dorado2100G2(self):
|
||||
connection_data = self.driver.initialize_connection(FakeVolume,
|
||||
FakeConnector)
|
||||
iscsi_properties = connection_data['data']
|
||||
|
||||
self.assertEquals(iscsi_properties['target_iqn'],
|
||||
FakeConfInfo['TargetIQN-Dorado2100G2'])
|
||||
self.assertEquals(iscsi_properties['target_portal'],
|
||||
FakeConfInfo['Initiator TargetIP'] + ':3260')
|
||||
self.assertEqual(MapInfo["DEV LUN ID"], FakeVolume['id'])
|
||||
self.assertEqual(MapInfo["INI Port Info"],
|
||||
FakeConnector['initiator'])
|
||||
|
||||
def _test_initialize_connection_for_Dorado5100(self):
|
||||
connection_data = self.driver.initialize_connection(FakeVolume,
|
||||
FakeConnector)
|
||||
iscsi_properties = connection_data['data']
|
||||
|
||||
self.assertEquals(iscsi_properties['target_iqn'],
|
||||
FakeConfInfo['TargetIQN-Dorado5100'])
|
||||
self.assertEquals(iscsi_properties['target_portal'],
|
||||
FakeConfInfo['Initiator TargetIP'] + ':3260')
|
||||
self.assertEqual(MapInfo["DEV LUN ID"], FakeVolume['id'])
|
||||
self.assertEqual(MapInfo["INI Port Info"],
|
||||
FakeConnector['initiator'])
|
||||
|
||||
def _test_terminate_connection(self):
|
||||
self.driver.terminate_connection(FakeVolume, FakeConnector)
|
||||
self.assertEqual(MapInfo["DEV LUN ID"], None)
|
||||
self.assertEqual(MapInfo["Host LUN ID"], None)
|
||||
self.assertEqual(MapInfo["INI Port Info"], None)
|
||||
|
||||
def _test_get_get_volume_stats(self):
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
|
||||
fakecapacity = float(FakePoolInfo['Free Capacity']) / 1024
|
||||
self.assertEqual(stats['free_capacity_gb'], fakecapacity)
|
||||
|
||||
|
||||
class FakeHuaweiStorage(huawei_iscsi.HuaweiISCSIDriver):
|
||||
"""Fake Huawei Storage, Rewrite some methods of HuaweiISCSIDriver."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FakeHuaweiStorage, self).__init__(*args, **kwargs)
|
||||
self._test_flg = None
|
||||
|
||||
def _execute_cli(self, cmdIn):
|
||||
cmd = cmdIn.split(' ')[0].lower()
|
||||
if cmd == 'showsys':
|
||||
if ((self._test_flg == 'check_for_fail') or
|
||||
(self._test_flg == 'check_for_T')):
|
||||
out = """/>showsys
|
||||
==========================================================================
|
||||
System Information
|
||||
--------------------------------------------------------------------------
|
||||
System Name | SN_S5500T-xu-0123456789
|
||||
Device Type | Oceanstor S5500T
|
||||
Current System Mode | Double Controllers Normal
|
||||
Mirroring Link Status | Link Up
|
||||
Location |
|
||||
Time | 2013-01-01 01:01:01
|
||||
Product Version | V100R005C00
|
||||
===========================================================================
|
||||
"""
|
||||
elif self._test_flg == 'check_for_Dorado2100G2':
|
||||
out = """/>showsys
|
||||
==========================================================================
|
||||
System Information
|
||||
--------------------------------------------------------------------------
|
||||
System Name | SN_Dorado2100_G2
|
||||
Device Type | Oceanstor Dorado2100 G2
|
||||
Current System Mode | Double Controllers Normal
|
||||
Mirroring Link Status | Link Up
|
||||
Location |
|
||||
Time | 2013-01-01 01:01:01
|
||||
Product Version | V100R001C00
|
||||
===========================================================================
|
||||
"""
|
||||
elif self._test_flg == 'check_for_Dorado5100':
|
||||
out = """/>showsys
|
||||
==========================================================================
|
||||
System Information
|
||||
--------------------------------------------------------------------------
|
||||
System Name | SN_Dorado5100
|
||||
Device Type | Oceanstor Dorado5100
|
||||
Current System Mode | Double Controllers Normal
|
||||
Mirroring Link Status | Link Up
|
||||
Location |
|
||||
Time | 2013-01-01 01:01:01
|
||||
Product Version | V100R001C00
|
||||
===========================================================================
|
||||
"""
|
||||
elif cmd == 'addhostmap':
|
||||
MapInfo['DEV LUN ID'] = LUNInfo['ID']
|
||||
MapInfo['LUN WWN'] = LUNInfo['LUN WWN']
|
||||
MapInfo['Host LUN ID'] = '0'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showhostmap':
|
||||
if MapInfo['DEV LUN ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
else:
|
||||
out = """/>showhostmap
|
||||
==========================================================================
|
||||
Map Information
|
||||
--------------------------------------------------------------------------
|
||||
Map ID Working Controller Dev LUN ID LUN WWN Host LUN ID Mapped to \
|
||||
RAID ID Dev LUN Cap(MB) Map Type Whether Command LUN Pool ID
|
||||
---------------------------------------------------------------------------
|
||||
2147483649 %s %s %s %s Host: %s %s %s HOST No --
|
||||
===========================================================================
|
||||
""" % (LUNInfo['Worker Controller'], LUNInfo['ID'], LUNInfo['LUN WWN'],
|
||||
MapInfo['Host ID'], MapInfo['Host ID'], LUNInfo['RAID Group ID'],
|
||||
str(int(LUNInfo['Size']) * 1024))
|
||||
|
||||
elif cmd == 'delhostmap':
|
||||
MapInfo['DEV LUN ID'] = None
|
||||
MapInfo['LUN WWN'] = None
|
||||
MapInfo['Host LUN ID'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'createsnapshot':
|
||||
SnapshotInfo['Source LUN ID'] = LUNInfo['ID']
|
||||
SnapshotInfo['Source LUN Name'] = LUNInfo['Name']
|
||||
SnapshotInfo['ID'] = FakeSnapshot['id']
|
||||
SnapshotInfo['Name'] = self._name_translate(FakeSnapshot['name'])
|
||||
SnapshotInfo['Status'] = 'Disable'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'actvsnapshot':
|
||||
SnapshotInfo['Status'] = 'Active'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'disablesnapshot':
|
||||
SnapshotInfo['Status'] = 'Disable'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'delsnapshot':
|
||||
SnapshotInfo['Source LUN ID'] = None
|
||||
SnapshotInfo['Source LUN Name'] = None
|
||||
SnapshotInfo['ID'] = None
|
||||
SnapshotInfo['Name'] = None
|
||||
SnapshotInfo['Status'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showsnapshot':
|
||||
if SnapshotInfo['ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
else:
|
||||
out = """/>showsnapshot
|
||||
==========================================================================
|
||||
Snapshot Information
|
||||
--------------------------------------------------------------------------
|
||||
Name ID Type Status Time Stamp
|
||||
--------------------------------------------------------------------------
|
||||
%s %s Public %s 2013-01-15 14:21:13
|
||||
==========================================================================
|
||||
""" % (SnapshotInfo['Name'], SnapshotInfo['ID'], SnapshotInfo['Status'])
|
||||
|
||||
elif cmd == 'showlunsnapshot':
|
||||
if SnapshotInfo['ID'] is None:
|
||||
out = """Current LUN is not a source LUN"""
|
||||
else:
|
||||
out = """/>showlunsnapshot -lun 2
|
||||
==========================================================================
|
||||
Snapshot of LUN
|
||||
--------------------------------------------------------------------------
|
||||
Name ID Type Status Time Stamp
|
||||
--------------------------------------------------------------------------
|
||||
%s %s Public %s 2013-01-15 14:17:19
|
||||
==========================================================================
|
||||
""" % (SnapshotInfo['Name'], SnapshotInfo['ID'], SnapshotInfo['Status'])
|
||||
|
||||
elif cmd == 'createlun':
|
||||
if LUNInfo['ID'] is None:
|
||||
LUNInfo['Name'] = self._name_translate(FakeVolume['name'])
|
||||
LUNInfo['ID'] = FakeVolume['id']
|
||||
LUNInfo['Size'] = FakeVolume['size']
|
||||
LUNInfo['LUN WWN'] = FakeVolume['wwn']
|
||||
LUNInfo['Owner Controller'] = 'A'
|
||||
LUNInfo['Worker Controller'] = 'A'
|
||||
LUNInfo['RAID Group ID'] = FakePoolInfo['ID']
|
||||
else:
|
||||
LUNInfoCopy['Name'] = \
|
||||
self._name_translate(FakeVolumeCopy['name'])
|
||||
LUNInfoCopy['ID'] = FakeVolumeCopy['ID']
|
||||
LUNInfoCopy['Size'] = FakeVolumeCopy['size']
|
||||
LUNInfoCopy['LUN WWN'] = FakeVolumeCopy['wwn']
|
||||
LUNInfoCopy['Owner Controller'] = 'A'
|
||||
LUNInfoCopy['Worker Controller'] = 'A'
|
||||
LUNInfoCopy['RAID Group ID'] = FakePoolInfo['ID']
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'dellun':
|
||||
LUNInfo['Name'] = None
|
||||
LUNInfo['ID'] = None
|
||||
LUNInfo['Size'] = None
|
||||
LUNInfo['LUN WWN'] = None
|
||||
LUNInfo['Owner Controller'] = None
|
||||
LUNInfo['Worker Controller'] = None
|
||||
LUNInfo['RAID Group ID'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showlun':
|
||||
if LUNInfo['ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
elif LUNInfoCopy['ID'] is None:
|
||||
if ((self._test_flg == 'check_for_fail') or
|
||||
(self._test_flg == 'check_for_T')):
|
||||
out = """/>showlun
|
||||
===========================================================================
|
||||
LUN Information
|
||||
---------------------------------------------------------------------------
|
||||
ID RAID Group ID Disk Pool ID Status Controller Visible Capacity(MB) \
|
||||
LUN Name Stripe Unit Size(KB) Lun Type
|
||||
---------------------------------------------------------------------------
|
||||
%s %s -- Normal %s %s %s 64 THICK
|
||||
===========================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['RAID Group ID'], LUNInfo['Owner Controller'],
|
||||
str(int(LUNInfo['Size']) * 1024), LUNInfo['Name'])
|
||||
elif self._test_flg == 'check_for_Dorado2100G2':
|
||||
out = """/>showlun
|
||||
===========================================================================
|
||||
LUN Information
|
||||
---------------------------------------------------------------------------
|
||||
ID Status Controller Visible Capacity(MB) LUN Name Lun Type
|
||||
---------------------------------------------------------------------------
|
||||
%s Normal %s %s %s THICK
|
||||
===========================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['Owner Controller'],
|
||||
str(int(LUNInfo['Size']) * 1024), LUNInfo['Name'])
|
||||
elif self._test_flg == 'check_for_Dorado5100':
|
||||
out = """/>showlun
|
||||
===========================================================================
|
||||
LUN Information
|
||||
---------------------------------------------------------------------------
|
||||
ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name
|
||||
Strip Unit Size(KB) Lun Type
|
||||
---------------------------------------------------------------------------
|
||||
%s %s Normal %s %s %s 64 THICK
|
||||
===========================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['RAID Group ID'],
|
||||
LUNInfo['Owner Controller'], str(int(LUNInfo['Size']) * 1024),
|
||||
LUNInfo['Name'])
|
||||
else:
|
||||
if ((self._test_flg == 'check_for_fail') or
|
||||
(self._test_flg == 'check_for_T')):
|
||||
out = """/>showlun
|
||||
============================================================================
|
||||
LUN Information
|
||||
----------------------------------------------------------------------------
|
||||
ID RAID Group ID Disk Pool ID Status Controller Visible Capacity(MB)\
|
||||
LUN Name Stripe Unit Size(KB) Lun Type
|
||||
----------------------------------------------------------------------------
|
||||
%s %s -- Normal %s %s %s 64 THICK
|
||||
%s %s -- Normal %s %s %s 64 THICK
|
||||
============================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['RAID Group ID'], LUNInfo['Owner Controller'],
|
||||
str(int(LUNInfo['Size']) * 1024), LUNInfo['Name'], LUNInfoCopy['ID'],
|
||||
LUNInfoCopy['RAID Group ID'], LUNInfoCopy['Owner Controller'],
|
||||
str(int(LUNInfoCopy['Size']) * 1024), LUNInfoCopy['Name'])
|
||||
elif self._test_flg == 'check_for_Dorado2100G2':
|
||||
out = """/>showlun
|
||||
===========================================================================
|
||||
LUN Information
|
||||
---------------------------------------------------------------------------
|
||||
ID Status Controller Visible Capacity(MB) LUN Name Lun Type
|
||||
---------------------------------------------------------------------------
|
||||
%s Normal %s %s %s THICK
|
||||
%s Normal %s %s %s THICK
|
||||
===========================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['Owner Controller'],
|
||||
str(int(LUNInfo['Size']) * 1024), LUNInfo['Name'],
|
||||
LUNInfoCopy['ID'], LUNInfoCopy['Owner Controller'],
|
||||
str(int(LUNInfoCopy['Size']) * 1024), LUNInfoCopy['Name'])
|
||||
elif self._test_flg == 'check_for_Dorado5100':
|
||||
out = """/>showlun
|
||||
===========================================================================
|
||||
LUN Information
|
||||
---------------------------------------------------------------------------
|
||||
ID RAIDgroup ID Status Controller Visible Capacity(MB) LUN Name \
|
||||
Strip Unit Size(KB) Lun Type
|
||||
---------------------------------------------------------------------------
|
||||
%s %s Normal %s %s %s 64 THICK
|
||||
%s %s Norma %s %s %s 64 THICK
|
||||
===========================================================================
|
||||
""" % (LUNInfo['ID'], LUNInfo['RAID Group ID'], LUNInfo['Owner Controller'],
|
||||
str(int(LUNInfo['Size']) * 1024), LUNInfo['Name'],
|
||||
LUNInfoCopy['ID'], LUNInfoCopy['RAID Group ID'],
|
||||
LUNInfoCopy['Owner Controller'], str(int(LUNInfoCopy['Size']) * 1024),
|
||||
LUNInfoCopy['Name'])
|
||||
|
||||
elif cmd == 'createhostgroup':
|
||||
MapInfo['Host Group ID'] = '1'
|
||||
MapInfo['Host Group Name'] = FakeConfInfo['HostGroup']
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showhostgroup':
|
||||
if MapInfo['Host Group ID'] is None:
|
||||
out = """/>showhostgroup
|
||||
============================================================
|
||||
Host Group Information
|
||||
------------------------------------------------------------
|
||||
Host Group ID Name File Engine Cluster
|
||||
------------------------------------------------------------
|
||||
0 Default Group NO
|
||||
============================================================
|
||||
"""
|
||||
else:
|
||||
out = """/>showhostgroup
|
||||
============================================================
|
||||
Host Group Information
|
||||
------------------------------------------------------------
|
||||
Host Group ID Name File Engine Cluster
|
||||
------------------------------------------------------------
|
||||
0 Default Group NO
|
||||
%s %s NO
|
||||
============================================================
|
||||
""" % (MapInfo['Host Group ID'], MapInfo['Host Group Name'])
|
||||
|
||||
elif cmd == 'addhost':
|
||||
MapInfo['Host ID'] = '1'
|
||||
MapInfo['Host Name'] = FakeConfInfo['HostnamePrefix'] + \
|
||||
str(hash(FakeConnector['initiator']))
|
||||
MapInfo['Os Type'] = 'Linux'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'delhost':
|
||||
MapInfo['Host ID'] = None
|
||||
MapInfo['Host Name'] = None
|
||||
MapInfo['Os Type'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showhost':
|
||||
if MapInfo['Host ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
else:
|
||||
out = """/>showhost
|
||||
=======================================================
|
||||
Host Information
|
||||
-------------------------------------------------------
|
||||
Host ID Host Name Host Group ID Os Type
|
||||
-------------------------------------------------------
|
||||
%s %s %s Linux
|
||||
=======================================================
|
||||
""" % (MapInfo['Host ID'], MapInfo['Host Name'], MapInfo['Host Group ID'])
|
||||
|
||||
elif cmd == 'createluncopy':
|
||||
LUNCopy['Name'] = LUNInfoCopy['Name']
|
||||
LUNCopy['ID'] = FakeLUNCopy['ID']
|
||||
LUNCopy['Type'] = FakeLUNCopy['Type']
|
||||
LUNCopy['State'] = FakeLUNCopy['State']
|
||||
LUNCopy['Status'] = FakeLUNCopy['Status']
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'delluncopy':
|
||||
LUNCopy['Name'] = None
|
||||
LUNCopy['ID'] = None
|
||||
LUNCopy['Type'] = None
|
||||
LUNCopy['State'] = None
|
||||
LUNCopy['Status'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'chgluncopystatus':
|
||||
LUNCopy['State'] = 'Complete'
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showluncopy':
|
||||
if LUNCopy['ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
else:
|
||||
out = """/>showluncopy
|
||||
============================================================================
|
||||
LUN Copy Information
|
||||
----------------------------------------------------------------------------
|
||||
LUN Copy Name LUN Copy ID Type LUN Copy State LUN Copy Status
|
||||
----------------------------------------------------------------------------
|
||||
%s %s %s %s %s
|
||||
============================================================================
|
||||
""" % (LUNCopy['Name'], LUNCopy['ID'], LUNCopy['Type'],
|
||||
LUNCopy['State'], LUNCopy['Status'])
|
||||
|
||||
elif cmd == 'showiscsitgtname':
|
||||
if ((self._test_flg == 'check_for_fail') or
|
||||
(self._test_flg == 'check_for_T')):
|
||||
out = """/>showiscsitgtname
|
||||
============================================================================
|
||||
ISCSI Name
|
||||
----------------------------------------------------------------------------
|
||||
Iscsi Name | %s
|
||||
============================================================================
|
||||
""" % FakeConfInfo['TargetIQN']
|
||||
elif (self._test_flg == 'check_for_Dorado2100G2' or
|
||||
self._test_flg == 'check_for_Dorado5100'):
|
||||
out = """/>showiscsitgtname
|
||||
============================================================================
|
||||
ISCSI Name
|
||||
----------------------------------------------------------------------------
|
||||
Iscsi Name | %s
|
||||
============================================================================
|
||||
""" % FakeConfInfo['TargetIQN']
|
||||
|
||||
elif cmd == 'showiscsiip':
|
||||
out = """/>showiscsiip
|
||||
============================================================================
|
||||
iSCSI IP Information
|
||||
----------------------------------------------------------------------------
|
||||
Controller ID Interface Module ID Port ID IP Address Mask
|
||||
----------------------------------------------------------------------------
|
||||
A 0 P1 %s 255.255.255.0
|
||||
============================================================================
|
||||
""" % FakeConfInfo['Initiator TargetIP']
|
||||
|
||||
elif cmd == 'addhostport':
|
||||
MapInfo['INI Port ID'] = HostPort['ID']
|
||||
MapInfo['INI Port Name'] = HostPort['Name']
|
||||
MapInfo['INI Port Info'] = HostPort['Info']
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'delhostport':
|
||||
MapInfo['INI Port ID'] = None
|
||||
MapInfo['INI Port Name'] = None
|
||||
MapInfo['INI Port Info'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showhostport':
|
||||
if MapInfo['INI Port ID'] is None:
|
||||
out = 'command operates successfully, but no information.'
|
||||
else:
|
||||
out = """/>showhostport -host 3
|
||||
==============================================================================
|
||||
Host Port Information
|
||||
------------------------------------------------------------------------------
|
||||
Port ID Port Name Port Information Port Type Host ID \
|
||||
Link Status Multipath Type
|
||||
------------------------------------------------------------------------------
|
||||
%s %s %s ISCSITGT %s Unconnected Default
|
||||
==============================================================================
|
||||
""" % (MapInfo['INI Port ID'], MapInfo['INI Port Name'],
|
||||
MapInfo['INI Port Info'], MapInfo['Host ID'])
|
||||
|
||||
elif cmd == 'addiscsiini':
|
||||
HostPort['ID'] = '1'
|
||||
HostPort['Name'] = 'iSCSIInitiator001'
|
||||
HostPort['Info'] = FakeConfInfo['Initiator Name']
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'deliscsiini':
|
||||
HostPort['ID'] = None
|
||||
HostPort['Name'] = None
|
||||
HostPort['Info'] = None
|
||||
out = 'command operates successfully'
|
||||
|
||||
elif cmd == 'showiscsiini':
|
||||
if HostPort['ID'] is None:
|
||||
out = 'Error: The parameter is wrong.'
|
||||
else:
|
||||
out = """/>showiscsiini -ini iqn.1993-08.org\
|
||||
.debian:01:503629a9d3f
|
||||
========================================================
|
||||
Initiator Information
|
||||
--------------------------------------------------------
|
||||
Initiator Name Chap Status
|
||||
--------------------------------------------------------
|
||||
%s Disable
|
||||
========================================================
|
||||
""" % (HostPort['Info'])
|
||||
|
||||
elif cmd == 'showrg':
|
||||
out = """/>showrg
|
||||
=====================================================================
|
||||
RAID Group Information
|
||||
---------------------------------------------------------------------
|
||||
ID Level Status Free Capacity(MB) Disk List Name
|
||||
---------------------------------------------------------------------
|
||||
0 RAID6 Normal 1024 0,0;0,2;0,4;0,5;0,6;0,7; RAID003
|
||||
%s %s %s %s %s %s
|
||||
=====================================================================
|
||||
""" % (FakePoolInfo['ID'], FakePoolInfo['Level'],
|
||||
FakePoolInfo['Status'], FakePoolInfo['Free Capacity'],
|
||||
FakePoolInfo['Disk List'], FakePoolInfo['Name'])
|
||||
|
||||
elif cmd == 'showrespool':
|
||||
out = """/>showrespool
|
||||
============================================================================
|
||||
Resource Pool Information
|
||||
----------------------------------------------------------------------------
|
||||
Pool ID Size(MB) Usage(MB) Valid Size(MB) Alarm Threshold(%)
|
||||
----------------------------------------------------------------------------
|
||||
A 5130.0 0.0 5130.0 80
|
||||
B 3082.0 0.0 3082.0 80
|
||||
============================================================================
|
||||
"""
|
||||
|
||||
elif cmd == 'chglun':
|
||||
out = 'command operates successfully'
|
||||
|
||||
out = out.replace('\n', '\r\n')
|
||||
return out
|
||||
|
||||
def _get_lun_controller(self, lunid):
|
||||
pass
|
|
@ -1,190 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os.path
|
||||
import shutil
|
||||
import string
|
||||
import tempfile
|
||||
|
||||
from manila.brick.iscsi import iscsi
|
||||
from manila import test
|
||||
from manila.volume import utils as volume_utils
|
||||
|
||||
|
||||
class TargetAdminTestCase(object):
|
||||
|
||||
def setUp(self):
|
||||
self.cmds = []
|
||||
|
||||
self.tid = 1
|
||||
self.target_name = 'iqn.2011-09.org.foo.bar:blaa'
|
||||
self.lun = 10
|
||||
self.path = '/foo'
|
||||
self.vol_id = 'blaa'
|
||||
|
||||
self.script_template = None
|
||||
self.stubs.Set(os.path, 'isfile', lambda _: True)
|
||||
self.stubs.Set(os, 'unlink', lambda _: '')
|
||||
self.stubs.Set(iscsi.TgtAdm, '_get_target', self.fake_get_target)
|
||||
self.stubs.Set(iscsi.LioAdm, '_get_target', self.fake_get_target)
|
||||
self.stubs.Set(iscsi.LioAdm, '__init__', self.fake_init)
|
||||
|
||||
def fake_init(obj):
|
||||
return
|
||||
|
||||
def fake_get_target(obj, iqn):
|
||||
return 1
|
||||
|
||||
def get_script_params(self):
|
||||
return {'tid': self.tid,
|
||||
'target_name': self.target_name,
|
||||
'lun': self.lun,
|
||||
'path': self.path}
|
||||
|
||||
def get_script(self):
|
||||
return self.script_template % self.get_script_params()
|
||||
|
||||
def fake_execute(self, *cmd, **kwargs):
|
||||
self.cmds.append(string.join(cmd))
|
||||
return "", None
|
||||
|
||||
def clear_cmds(self):
|
||||
self.cmds = []
|
||||
|
||||
def verify_cmds(self, cmds):
|
||||
self.assertEqual(len(cmds), len(self.cmds))
|
||||
for a, b in zip(cmds, self.cmds):
|
||||
self.assertEqual(a, b)
|
||||
|
||||
def verify(self):
|
||||
script = self.get_script()
|
||||
cmds = []
|
||||
for line in script.split('\n'):
|
||||
if not line.strip():
|
||||
continue
|
||||
cmds.append(line)
|
||||
self.verify_cmds(cmds)
|
||||
|
||||
def run_commands(self):
|
||||
tgtadm = iscsi.get_target_admin()
|
||||
tgtadm.set_execute(self.fake_execute)
|
||||
tgtadm.create_iscsi_target(self.target_name, self.tid,
|
||||
self.lun, self.path)
|
||||
tgtadm.show_target(self.tid, iqn=self.target_name)
|
||||
tgtadm.remove_iscsi_target(self.tid, self.lun, self.vol_id)
|
||||
|
||||
def test_target_admin(self):
|
||||
self.clear_cmds()
|
||||
self.run_commands()
|
||||
self.verify()
|
||||
|
||||
|
||||
class TgtAdmTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TgtAdmTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.persist_tempdir = tempfile.mkdtemp()
|
||||
self.flags(iscsi_helper='tgtadm')
|
||||
self.flags(volumes_dir=self.persist_tempdir)
|
||||
self.script_template = "\n".join([
|
||||
'tgt-admin --update iqn.2011-09.org.foo.bar:blaa',
|
||||
'tgt-admin --force '
|
||||
'--delete iqn.2010-10.org.openstack:volume-blaa'])
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
shutil.rmtree(self.persist_tempdir)
|
||||
except OSError:
|
||||
pass
|
||||
super(TgtAdmTestCase, self).tearDown()
|
||||
|
||||
|
||||
class IetAdmTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IetAdmTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.flags(iscsi_helper='ietadm')
|
||||
self.script_template = "\n".join([
|
||||
'ietadm --op new --tid=%(tid)s --params Name=%(target_name)s',
|
||||
'ietadm --op new --tid=%(tid)s --lun=%(lun)s '
|
||||
'--params Path=%(path)s,Type=fileio',
|
||||
'ietadm --op show --tid=%(tid)s',
|
||||
'ietadm --op delete --tid=%(tid)s --lun=%(lun)s',
|
||||
'ietadm --op delete --tid=%(tid)s'])
|
||||
|
||||
|
||||
class IetAdmBlockIOTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IetAdmBlockIOTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.flags(iscsi_helper='ietadm')
|
||||
self.flags(iscsi_iotype='blockio')
|
||||
self.script_template = "\n".join([
|
||||
'ietadm --op new --tid=%(tid)s --params Name=%(target_name)s',
|
||||
'ietadm --op new --tid=%(tid)s --lun=%(lun)s '
|
||||
'--params Path=%(path)s,Type=blockio',
|
||||
'ietadm --op show --tid=%(tid)s',
|
||||
'ietadm --op delete --tid=%(tid)s --lun=%(lun)s',
|
||||
'ietadm --op delete --tid=%(tid)s'])
|
||||
|
||||
|
||||
class IetAdmFileIOTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IetAdmFileIOTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.flags(iscsi_helper='ietadm')
|
||||
self.flags(iscsi_iotype='fileio')
|
||||
self.script_template = "\n".join([
|
||||
'ietadm --op new --tid=%(tid)s --params Name=%(target_name)s',
|
||||
'ietadm --op new --tid=%(tid)s --lun=%(lun)s '
|
||||
'--params Path=%(path)s,Type=fileio',
|
||||
'ietadm --op show --tid=%(tid)s',
|
||||
'ietadm --op delete --tid=%(tid)s --lun=%(lun)s',
|
||||
'ietadm --op delete --tid=%(tid)s'])
|
||||
|
||||
|
||||
class IetAdmAutoIOTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IetAdmAutoIOTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.stubs.Set(volume_utils, 'is_block', lambda _: True)
|
||||
self.flags(iscsi_helper='ietadm')
|
||||
self.flags(iscsi_iotype='auto')
|
||||
self.script_template = "\n".join([
|
||||
'ietadm --op new --tid=%(tid)s --params Name=%(target_name)s',
|
||||
'ietadm --op new --tid=%(tid)s --lun=%(lun)s '
|
||||
'--params Path=%(path)s,Type=blockio',
|
||||
'ietadm --op show --tid=%(tid)s',
|
||||
'ietadm --op delete --tid=%(tid)s --lun=%(lun)s',
|
||||
'ietadm --op delete --tid=%(tid)s'])
|
||||
|
||||
|
||||
class LioAdmTestCase(test.TestCase, TargetAdminTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LioAdmTestCase, self).setUp()
|
||||
TargetAdminTestCase.setUp(self)
|
||||
self.persist_tempdir = tempfile.mkdtemp()
|
||||
self.flags(iscsi_helper='lioadm')
|
||||
self.script_template = "\n".join([
|
||||
'rtstool create '
|
||||
'/foo iqn.2011-09.org.foo.bar:blaa test_id test_pass',
|
||||
'rtstool delete iqn.2010-10.org.openstack:volume-blaa'])
|
File diff suppressed because it is too large
Load Diff
|
@ -1,695 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 NetApp, Inc.
|
||||
# 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.
|
||||
"""Unit tests for the NetApp-specific NFS driver module (netapp_nfs)."""
|
||||
|
||||
from manila import context
|
||||
from manila import exception
|
||||
from manila import test
|
||||
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.netapp import api
|
||||
from manila.volume.drivers.netapp import nfs as netapp_nfs
|
||||
from manila.volume.drivers import nfs
|
||||
from lxml import etree
|
||||
from mox import IgnoreArg
|
||||
from mox import IsA
|
||||
from mox import MockObject
|
||||
|
||||
import mox
|
||||
import suds
|
||||
import types
|
||||
|
||||
|
||||
def create_configuration():
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
return configuration
|
||||
|
||||
|
||||
class FakeVolume(object):
|
||||
def __init__(self, size=0):
|
||||
self.size = size
|
||||
self.id = hash(self)
|
||||
self.name = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.__dict__[key]
|
||||
|
||||
|
||||
class FakeSnapshot(object):
|
||||
def __init__(self, volume_size=0):
|
||||
self.volume_name = None
|
||||
self.name = None
|
||||
self.volume_id = None
|
||||
self.volume_size = volume_size
|
||||
self.user_id = None
|
||||
self.status = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.__dict__[key]
|
||||
|
||||
|
||||
class FakeResponce(object):
|
||||
def __init__(self, status):
|
||||
"""
|
||||
:param status: Either 'failed' or 'passed'
|
||||
"""
|
||||
self.Status = status
|
||||
|
||||
if status == 'failed':
|
||||
self.Reason = 'Sample error'
|
||||
|
||||
|
||||
class NetappNfsDriverTestCase(test.TestCase):
|
||||
"""Test case for NetApp specific NFS clone driver."""
|
||||
|
||||
def setUp(self):
|
||||
self._mox = mox.Mox()
|
||||
self._driver = netapp_nfs.NetAppNFSDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def tearDown(self):
|
||||
self._mox.UnsetStubs()
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
required_flags = ['netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.StubOutWithMock(nfs.NfsDriver, 'check_for_setup_error')
|
||||
nfs.NfsDriver.check_for_setup_error()
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_snapshot(self):
|
||||
"""Test snapshot can be created and deleted."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.create_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
"""Tests volume creation from snapshot."""
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
volume = FakeVolume(1)
|
||||
snapshot = FakeSnapshot(2)
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.create_volume_from_snapshot,
|
||||
volume,
|
||||
snapshot)
|
||||
|
||||
snapshot = FakeSnapshot(1)
|
||||
|
||||
location = '127.0.0.1:/nfs'
|
||||
expected_result = {'provider_location': location}
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
mox.StubOutWithMock(drv, '_get_volume_location')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
drv._get_volume_location(IgnoreArg()).AndReturn(location)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
loc = drv.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
self.assertEquals(loc, expected_result)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_delete_snapshot_mock(self, snapshot_exists):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_provider_location')
|
||||
mox.StubOutWithMock(drv, '_volume_not_present')
|
||||
|
||||
if snapshot_exists:
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
mox.StubOutWithMock(drv, '_get_volume_path')
|
||||
|
||||
drv._get_provider_location(IgnoreArg())
|
||||
drv._volume_not_present(IgnoreArg(),
|
||||
IgnoreArg()).AndReturn(not snapshot_exists)
|
||||
|
||||
if snapshot_exists:
|
||||
drv._get_volume_path(IgnoreArg(), IgnoreArg())
|
||||
drv._execute('rm', None, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
return mox
|
||||
|
||||
def test_delete_existing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(True)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_missing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(False)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
drv._client = MockObject(suds.client.Client)
|
||||
drv._client.factory = MockObject(suds.client.Factory)
|
||||
drv._client.service = MockObject(suds.client.ServiceSelector)
|
||||
|
||||
# ApiProxy() method is generated by ServiceSelector at runtime from the
|
||||
# XML, so mocking is impossible.
|
||||
setattr(drv._client.service,
|
||||
'ApiProxy',
|
||||
types.MethodType(lambda *args, **kwargs: FakeResponce(status),
|
||||
suds.client.ServiceSelector))
|
||||
mox.StubOutWithMock(drv, '_get_host_id')
|
||||
mox.StubOutWithMock(drv, '_get_full_export_path')
|
||||
|
||||
drv._get_host_id(IgnoreArg()).AndReturn('10')
|
||||
drv._get_full_export_path(IgnoreArg(), IgnoreArg()).AndReturn('/nfs')
|
||||
|
||||
return mox
|
||||
|
||||
def test_successfull_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('passed')
|
||||
# set required flags
|
||||
setattr(drv.configuration, 'synchronous_snapshot_create', False)
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_failed_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('failed')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv._clone_volume,
|
||||
volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_cloned_volume_size_fail(self):
|
||||
volume_clone_fail = FakeVolume(1)
|
||||
volume_src = FakeVolume(2)
|
||||
try:
|
||||
self._driver.create_cloned_volume(volume_clone_fail,
|
||||
volume_src)
|
||||
raise AssertionError()
|
||||
except exception.ManilaException:
|
||||
pass
|
||||
|
||||
|
||||
class NetappCmodeNfsDriverTestCase(test.TestCase):
|
||||
"""Test case for NetApp C Mode specific NFS clone driver"""
|
||||
|
||||
def setUp(self):
|
||||
self._mox = mox.Mox()
|
||||
self._custom_setup()
|
||||
|
||||
def _custom_setup(self):
|
||||
self._driver = netapp_nfs.NetAppCmodeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def tearDown(self):
|
||||
self._mox.UnsetStubs()
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
required_flags = [
|
||||
'netapp_wsdl_url',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_snapshot(self):
|
||||
"""Test snapshot can be created and deleted"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.create_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
"""Tests volume creation from snapshot"""
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
volume = FakeVolume(1)
|
||||
snapshot = FakeSnapshot(2)
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.create_volume_from_snapshot,
|
||||
volume,
|
||||
snapshot)
|
||||
|
||||
snapshot = FakeSnapshot(1)
|
||||
|
||||
location = '127.0.0.1:/nfs'
|
||||
expected_result = {'provider_location': location}
|
||||
mox.StubOutWithMock(drv, '_clone_volume')
|
||||
mox.StubOutWithMock(drv, '_get_volume_location')
|
||||
drv._clone_volume(IgnoreArg(), IgnoreArg(), IgnoreArg())
|
||||
drv._get_volume_location(IgnoreArg()).AndReturn(location)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
loc = drv.create_volume_from_snapshot(volume, snapshot)
|
||||
|
||||
self.assertEquals(loc, expected_result)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_delete_snapshot_mock(self, snapshot_exists):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_provider_location')
|
||||
mox.StubOutWithMock(drv, '_volume_not_present')
|
||||
|
||||
if snapshot_exists:
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
mox.StubOutWithMock(drv, '_get_volume_path')
|
||||
|
||||
drv._get_provider_location(IgnoreArg())
|
||||
drv._volume_not_present(IgnoreArg(), IgnoreArg())\
|
||||
.AndReturn(not snapshot_exists)
|
||||
|
||||
if snapshot_exists:
|
||||
drv._get_volume_path(IgnoreArg(), IgnoreArg())
|
||||
drv._execute('rm', None, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
return mox
|
||||
|
||||
def test_delete_existing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(True)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_missing_snapshot(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_delete_snapshot_mock(False)
|
||||
|
||||
drv.delete_snapshot(FakeSnapshot())
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
drv._client = MockObject(suds.client.Client)
|
||||
drv._client.factory = MockObject(suds.client.Factory)
|
||||
drv._client.service = MockObject(suds.client.ServiceSelector)
|
||||
# CloneNasFile method is generated by ServiceSelector at runtime from
|
||||
# the
|
||||
# XML, so mocking is impossible.
|
||||
setattr(drv._client.service,
|
||||
'CloneNasFile',
|
||||
types.MethodType(lambda *args, **kwargs: FakeResponce(status),
|
||||
suds.client.ServiceSelector))
|
||||
mox.StubOutWithMock(drv, '_get_host_ip')
|
||||
mox.StubOutWithMock(drv, '_get_export_path')
|
||||
|
||||
drv._get_host_ip(IgnoreArg()).AndReturn('127.0.0.1')
|
||||
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
|
||||
return mox
|
||||
|
||||
def test_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('passed')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_cloned_volume_size_fail(self):
|
||||
volume_clone_fail = FakeVolume(1)
|
||||
volume_src = FakeVolume(2)
|
||||
try:
|
||||
self._driver.create_cloned_volume(volume_clone_fail,
|
||||
volume_src)
|
||||
raise AssertionError()
|
||||
except exception.ManilaException:
|
||||
pass
|
||||
|
||||
|
||||
class NetappDirectCmodeNfsDriverTestCase(NetappCmodeNfsDriverTestCase):
|
||||
"""Test direct NetApp C Mode driver"""
|
||||
def _custom_setup(self):
|
||||
self._driver = netapp_nfs.NetAppDirectCmodeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
required_flags = [
|
||||
'netapp_transport_type',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
mox.StubOutWithMock(drv, '_do_custom_setup')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
drv._do_custom_setup(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_host_ip')
|
||||
mox.StubOutWithMock(drv, '_get_export_path')
|
||||
mox.StubOutWithMock(drv, '_get_if_info_by_ip')
|
||||
mox.StubOutWithMock(drv, '_get_vol_by_junc_vserver')
|
||||
mox.StubOutWithMock(drv, '_clone_file')
|
||||
|
||||
drv._get_host_ip(IgnoreArg()).AndReturn('127.0.0.1')
|
||||
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
|
||||
drv._get_if_info_by_ip('127.0.0.1').AndReturn(
|
||||
self._prepare_info_by_ip_response())
|
||||
drv._get_vol_by_junc_vserver('openstack', '/nfs').AndReturn('nfsvol')
|
||||
drv._clone_file('nfsvol', 'volume_name', 'clone_name',
|
||||
'openstack')
|
||||
return mox
|
||||
|
||||
def _prepare_info_by_ip_response(self):
|
||||
res = """<attributes-list>
|
||||
<net-interface-info>
|
||||
<address>127.0.0.1</address>
|
||||
<administrative-status>up</administrative-status>
|
||||
<current-node>fas3170rre-cmode-01</current-node>
|
||||
<current-port>e1b-1165</current-port>
|
||||
<data-protocols>
|
||||
<data-protocol>nfs</data-protocol>
|
||||
</data-protocols>
|
||||
<dns-domain-name>none</dns-domain-name>
|
||||
<failover-group/>
|
||||
<failover-policy>disabled</failover-policy>
|
||||
<firewall-policy>data</firewall-policy>
|
||||
<home-node>fas3170rre-cmode-01</home-node>
|
||||
<home-port>e1b-1165</home-port>
|
||||
<interface-name>nfs_data1</interface-name>
|
||||
<is-auto-revert>false</is-auto-revert>
|
||||
<is-home>true</is-home>
|
||||
<netmask>255.255.255.0</netmask>
|
||||
<netmask-length>24</netmask-length>
|
||||
<operational-status>up</operational-status>
|
||||
<role>data</role>
|
||||
<routing-group-name>c10.63.165.0/24</routing-group-name>
|
||||
<use-failover-group>disabled</use-failover-group>
|
||||
<vserver>openstack</vserver>
|
||||
</net-interface-info></attributes-list>"""
|
||||
response_el = etree.XML(res)
|
||||
return api.NaElement(response_el).get_children()
|
||||
|
||||
def test_clone_volume(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('pass')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
|
||||
class NetappDirect7modeNfsDriverTestCase(NetappDirectCmodeNfsDriverTestCase):
|
||||
"""Test direct NetApp C Mode driver"""
|
||||
def _custom_setup(self):
|
||||
self._driver = netapp_nfs.NetAppDirect7modeNfsDriver(
|
||||
configuration=create_configuration())
|
||||
|
||||
def test_check_for_setup_error(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
required_flags = [
|
||||
'netapp_transport_type',
|
||||
'netapp_login',
|
||||
'netapp_password',
|
||||
'netapp_server_hostname',
|
||||
'netapp_server_port']
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, None)
|
||||
# check exception raises when flags are not set
|
||||
self.assertRaises(exception.ManilaException,
|
||||
drv.check_for_setup_error)
|
||||
|
||||
# set required flags
|
||||
for flag in required_flags:
|
||||
setattr(drv.configuration, flag, 'val')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.check_for_setup_error()
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
# restore initial FLAGS
|
||||
for flag in required_flags:
|
||||
delattr(drv.configuration, flag)
|
||||
|
||||
def test_do_setup(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, 'check_for_setup_error')
|
||||
mox.StubOutWithMock(drv, '_get_client')
|
||||
mox.StubOutWithMock(drv, '_do_custom_setup')
|
||||
|
||||
drv.check_for_setup_error()
|
||||
drv._get_client()
|
||||
drv._do_custom_setup(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.do_setup(IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _prepare_clone_mock(self, status):
|
||||
drv = self._driver
|
||||
mox = self._mox
|
||||
|
||||
volume = FakeVolume()
|
||||
setattr(volume, 'provider_location', '127.0.0.1:/nfs')
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_export_path')
|
||||
mox.StubOutWithMock(drv, '_get_actual_path_for_export')
|
||||
mox.StubOutWithMock(drv, '_start_clone')
|
||||
mox.StubOutWithMock(drv, '_wait_for_clone_finish')
|
||||
if status == 'fail':
|
||||
mox.StubOutWithMock(drv, '_clear_clone')
|
||||
|
||||
drv._get_export_path(IgnoreArg()).AndReturn('/nfs')
|
||||
drv._get_actual_path_for_export(IgnoreArg()).AndReturn('/vol/vol1/nfs')
|
||||
drv._start_clone(IgnoreArg(), IgnoreArg()).AndReturn(('1', '2'))
|
||||
if status == 'fail':
|
||||
drv._wait_for_clone_finish('1', '2').AndRaise(
|
||||
api.NaApiError('error', 'error'))
|
||||
drv._clear_clone('1')
|
||||
else:
|
||||
drv._wait_for_clone_finish('1', '2')
|
||||
return mox
|
||||
|
||||
def test_clone_volume_clear(self):
|
||||
drv = self._driver
|
||||
mox = self._prepare_clone_mock('fail')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume_name = 'volume_name'
|
||||
clone_name = 'clone_name'
|
||||
volume_id = volume_name + str(hash(volume_name))
|
||||
try:
|
||||
drv._clone_volume(volume_name, clone_name, volume_id)
|
||||
except Exception as e:
|
||||
if isinstance(e, api.NaApiError):
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
|
||||
mox.VerifyAll()
|
|
@ -1,302 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2011 Nexenta Systems, Inc.
|
||||
# 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.
|
||||
"""
|
||||
Unit tests for OpenStack Manila volume driver
|
||||
"""
|
||||
|
||||
import base64
|
||||
import urllib2
|
||||
|
||||
import manila.flags
|
||||
from manila import test
|
||||
from manila.volume.drivers import nexenta
|
||||
from manila.volume.drivers.nexenta import jsonrpc
|
||||
from manila.volume.drivers.nexenta import volume
|
||||
|
||||
FLAGS = manila.flags.FLAGS
|
||||
|
||||
|
||||
class TestNexentaDriver(test.TestCase):
|
||||
TEST_VOLUME_NAME = 'volume1'
|
||||
TEST_VOLUME_NAME2 = 'volume2'
|
||||
TEST_SNAPSHOT_NAME = 'snapshot1'
|
||||
TEST_VOLUME_REF = {
|
||||
'name': TEST_VOLUME_NAME,
|
||||
'size': 1,
|
||||
}
|
||||
TEST_VOLUME_REF2 = {
|
||||
'name': TEST_VOLUME_NAME2,
|
||||
'size': 1,
|
||||
}
|
||||
TEST_SNAPSHOT_REF = {
|
||||
'name': TEST_SNAPSHOT_NAME,
|
||||
'volume_name': TEST_VOLUME_NAME,
|
||||
}
|
||||
|
||||
def __init__(self, method):
|
||||
super(TestNexentaDriver, self).__init__(method)
|
||||
|
||||
def setUp(self):
|
||||
super(TestNexentaDriver, self).setUp()
|
||||
self.flags(
|
||||
nexenta_host='1.1.1.1',
|
||||
nexenta_volume='manila',
|
||||
nexenta_target_prefix='iqn:',
|
||||
nexenta_target_group_prefix='manila/',
|
||||
nexenta_blocksize='8K',
|
||||
nexenta_sparse=True,
|
||||
)
|
||||
self.nms_mock = self.mox.CreateMockAnything()
|
||||
for mod in ['volume', 'zvol', 'iscsitarget',
|
||||
'stmf', 'scsidisk', 'snapshot']:
|
||||
setattr(self.nms_mock, mod, self.mox.CreateMockAnything())
|
||||
self.stubs.Set(jsonrpc, 'NexentaJSONProxy',
|
||||
lambda *_, **__: self.nms_mock)
|
||||
self.drv = volume.NexentaDriver()
|
||||
self.drv.do_setup({})
|
||||
|
||||
def test_setup_error(self):
|
||||
self.nms_mock.volume.object_exists('manila').AndReturn(True)
|
||||
self.mox.ReplayAll()
|
||||
self.drv.check_for_setup_error()
|
||||
|
||||
def test_setup_error_fail(self):
|
||||
self.nms_mock.volume.object_exists('manila').AndReturn(False)
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(LookupError, self.drv.check_for_setup_error)
|
||||
|
||||
def test_local_path(self):
|
||||
self.assertRaises(NotImplementedError, self.drv.local_path, '')
|
||||
|
||||
def test_create_volume(self):
|
||||
self.nms_mock.zvol.create('manila/volume1', '1G', '8K', True)
|
||||
self.mox.ReplayAll()
|
||||
self.drv.create_volume(self.TEST_VOLUME_REF)
|
||||
|
||||
def test_delete_volume(self):
|
||||
self.nms_mock.zvol.destroy('manila/volume1', '')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.delete_volume(self.TEST_VOLUME_REF)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
self.nms_mock.zvol.create_snapshot('manila/volume1', 'snapshot1', '')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.create_snapshot(self.TEST_SNAPSHOT_REF)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
self.nms_mock.zvol.clone('manila/volume1@snapshot1', 'manila/volume2')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.create_volume_from_snapshot(self.TEST_VOLUME_REF2,
|
||||
self.TEST_SNAPSHOT_REF)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
self.nms_mock.snapshot.destroy('manila/volume1@snapshot1', '')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF)
|
||||
|
||||
_CREATE_EXPORT_METHODS = [
|
||||
('iscsitarget', 'create_target', ({'target_name': 'iqn:volume1'},),
|
||||
u'Unable to create iscsi target\n'
|
||||
u' iSCSI target iqn.1986-03.com.sun:02:manila-volume1 already'
|
||||
u' configured\n'
|
||||
u' itadm create-target failed with error 17\n', ),
|
||||
('stmf', 'create_targetgroup', ('manila/volume1',),
|
||||
u'Unable to create targetgroup: stmfadm: manila/volume1:'
|
||||
u' already exists\n', ),
|
||||
('stmf', 'add_targetgroup_member', ('manila/volume1', 'iqn:volume1'),
|
||||
u'Unable to add member to targetgroup: stmfadm:'
|
||||
u' iqn.1986-03.com.sun:02:manila-volume1: already exists\n', ),
|
||||
('scsidisk', 'create_lu', ('manila/volume1', {}),
|
||||
u"Unable to create lu with zvol 'manila/volume1':\n"
|
||||
u" sbdadm: filename /dev/zvol/rdsk/manila/volume1: in use\n", ),
|
||||
('scsidisk', 'add_lun_mapping_entry', ('manila/volume1', {
|
||||
'target_group': 'manila/volume1', 'lun': '0'}),
|
||||
u"Unable to add view to zvol 'manila/volume1' (LUNs in use: ):\n"
|
||||
u" stmfadm: view entry exists\n", ),
|
||||
]
|
||||
|
||||
def _stub_export_method(self, module, method, args, error, fail=False):
|
||||
m = getattr(self.nms_mock, module)
|
||||
m = getattr(m, method)
|
||||
mock = m(*args)
|
||||
if fail:
|
||||
mock.AndRaise(nexenta.NexentaException(error))
|
||||
|
||||
def _stub_all_export_methods(self, fail=False):
|
||||
for params in self._CREATE_EXPORT_METHODS:
|
||||
self._stub_export_method(*params, fail=fail)
|
||||
|
||||
def test_create_export(self):
|
||||
self._stub_all_export_methods()
|
||||
self.mox.ReplayAll()
|
||||
retval = self.drv.create_export({}, self.TEST_VOLUME_REF)
|
||||
self.assertEquals(
|
||||
retval,
|
||||
{'provider_location':
|
||||
'%s:%s,1 %s%s 0' % (FLAGS.nexenta_host,
|
||||
FLAGS.nexenta_iscsi_target_portal_port,
|
||||
FLAGS.nexenta_target_prefix,
|
||||
self.TEST_VOLUME_NAME)})
|
||||
|
||||
def __get_test(i):
|
||||
def _test_create_export_fail(self):
|
||||
for params in self._CREATE_EXPORT_METHODS[:i]:
|
||||
self._stub_export_method(*params)
|
||||
self._stub_export_method(*self._CREATE_EXPORT_METHODS[i],
|
||||
fail=True)
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(nexenta.NexentaException,
|
||||
self.drv.create_export,
|
||||
{},
|
||||
self.TEST_VOLUME_REF)
|
||||
return _test_create_export_fail
|
||||
|
||||
for i in range(len(_CREATE_EXPORT_METHODS)):
|
||||
locals()['test_create_export_fail_%d' % i] = __get_test(i)
|
||||
|
||||
def test_ensure_export(self):
|
||||
self._stub_all_export_methods(fail=True)
|
||||
self.mox.ReplayAll()
|
||||
self.drv.ensure_export({}, self.TEST_VOLUME_REF)
|
||||
|
||||
def test_remove_export(self):
|
||||
self.nms_mock.scsidisk.delete_lu('manila/volume1')
|
||||
self.nms_mock.stmf.destroy_targetgroup('manila/volume1')
|
||||
self.nms_mock.iscsitarget.delete_target('iqn:volume1')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.remove_export({}, self.TEST_VOLUME_REF)
|
||||
|
||||
def test_remove_export_fail_0(self):
|
||||
self.nms_mock.scsidisk.delete_lu('manila/volume1')
|
||||
self.nms_mock.stmf.destroy_targetgroup(
|
||||
'manila/volume1').AndRaise(nexenta.NexentaException())
|
||||
self.nms_mock.iscsitarget.delete_target('iqn:volume1')
|
||||
self.mox.ReplayAll()
|
||||
self.drv.remove_export({}, self.TEST_VOLUME_REF)
|
||||
|
||||
def test_remove_export_fail_1(self):
|
||||
self.nms_mock.scsidisk.delete_lu('manila/volume1')
|
||||
self.nms_mock.stmf.destroy_targetgroup('manila/volume1')
|
||||
self.nms_mock.iscsitarget.delete_target(
|
||||
'iqn:volume1').AndRaise(nexenta.NexentaException())
|
||||
self.mox.ReplayAll()
|
||||
self.drv.remove_export({}, self.TEST_VOLUME_REF)
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
stats = {'size': '5368709120G',
|
||||
'used': '5368709120G',
|
||||
'available': '5368709120G',
|
||||
'health': 'ONLINE'}
|
||||
self.nms_mock.volume.get_child_props(
|
||||
FLAGS.nexenta_volume,
|
||||
'health|size|used|available').AndReturn(stats)
|
||||
self.mox.ReplayAll()
|
||||
stats = self.drv.get_volume_stats(True)
|
||||
self.assertEquals(stats['storage_protocol'], 'iSCSI')
|
||||
self.assertEquals(stats['volume_backend_name'], 'NexentaDriver')
|
||||
self.assertEquals(stats['total_capacity_gb'], 5368709120.0)
|
||||
self.assertEquals(stats['free_capacity_gb'], 5368709120.0)
|
||||
self.assertEquals(stats['reserved_percentage'], 0)
|
||||
self.assertEquals(stats['QoS_support'], False)
|
||||
|
||||
|
||||
class TestNexentaJSONRPC(test.TestCase):
|
||||
URL = 'http://example.com/'
|
||||
URL_S = 'https://example.com/'
|
||||
USER = 'user'
|
||||
PASSWORD = 'password'
|
||||
HEADERS = {'Authorization': 'Basic %s' % (
|
||||
base64.b64encode(':'.join((USER, PASSWORD))),),
|
||||
'Content-Type': 'application/json'}
|
||||
REQUEST = 'the request'
|
||||
|
||||
def setUp(self):
|
||||
super(TestNexentaJSONRPC, self).setUp()
|
||||
self.proxy = jsonrpc.NexentaJSONProxy(
|
||||
self.URL, self.USER, self.PASSWORD, auto=True)
|
||||
self.mox.StubOutWithMock(urllib2, 'Request', True)
|
||||
self.mox.StubOutWithMock(urllib2, 'urlopen')
|
||||
self.resp_mock = self.mox.CreateMockAnything()
|
||||
self.resp_info_mock = self.mox.CreateMockAnything()
|
||||
self.resp_mock.info().AndReturn(self.resp_info_mock)
|
||||
urllib2.urlopen(self.REQUEST).AndReturn(self.resp_mock)
|
||||
|
||||
def test_call(self):
|
||||
urllib2.Request(
|
||||
self.URL,
|
||||
'{"object": null, "params": ["arg1", "arg2"], "method": null}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
self.resp_info_mock.status = ''
|
||||
self.resp_mock.read().AndReturn(
|
||||
'{"error": null, "result": "the result"}')
|
||||
self.mox.ReplayAll()
|
||||
result = self.proxy('arg1', 'arg2')
|
||||
self.assertEquals("the result", result)
|
||||
|
||||
def test_call_deep(self):
|
||||
urllib2.Request(
|
||||
self.URL,
|
||||
'{"object": "obj1.subobj", "params": ["arg1", "arg2"],'
|
||||
' "method": "meth"}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
self.resp_info_mock.status = ''
|
||||
self.resp_mock.read().AndReturn(
|
||||
'{"error": null, "result": "the result"}')
|
||||
self.mox.ReplayAll()
|
||||
result = self.proxy.obj1.subobj.meth('arg1', 'arg2')
|
||||
self.assertEquals("the result", result)
|
||||
|
||||
def test_call_auto(self):
|
||||
urllib2.Request(
|
||||
self.URL,
|
||||
'{"object": null, "params": ["arg1", "arg2"], "method": null}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
urllib2.Request(
|
||||
self.URL_S,
|
||||
'{"object": null, "params": ["arg1", "arg2"], "method": null}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
self.resp_info_mock.status = 'EOF in headers'
|
||||
self.resp_mock.read().AndReturn(
|
||||
'{"error": null, "result": "the result"}')
|
||||
urllib2.urlopen(self.REQUEST).AndReturn(self.resp_mock)
|
||||
self.mox.ReplayAll()
|
||||
result = self.proxy('arg1', 'arg2')
|
||||
self.assertEquals("the result", result)
|
||||
|
||||
def test_call_error(self):
|
||||
urllib2.Request(
|
||||
self.URL,
|
||||
'{"object": null, "params": ["arg1", "arg2"], "method": null}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
self.resp_info_mock.status = ''
|
||||
self.resp_mock.read().AndReturn(
|
||||
'{"error": {"message": "the error"}, "result": "the result"}')
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(jsonrpc.NexentaJSONException,
|
||||
self.proxy, 'arg1', 'arg2')
|
||||
|
||||
def test_call_fail(self):
|
||||
urllib2.Request(
|
||||
self.URL,
|
||||
'{"object": null, "params": ["arg1", "arg2"], "method": null}',
|
||||
self.HEADERS).AndReturn(self.REQUEST)
|
||||
self.resp_info_mock.status = 'EOF in headers'
|
||||
self.proxy.auto = False
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(jsonrpc.NexentaJSONException,
|
||||
self.proxy, 'arg1', 'arg2')
|
|
@ -1,654 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 NetApp, Inc.
|
||||
# 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.
|
||||
"""Unit tests for the NFS driver module."""
|
||||
|
||||
import __builtin__
|
||||
import errno
|
||||
import os
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
import mox as mox_lib
|
||||
from mox import IgnoreArg
|
||||
from mox import IsA
|
||||
from mox import stubout
|
||||
|
||||
from manila import context
|
||||
from manila import exception
|
||||
from manila.exception import ProcessExecutionError
|
||||
from manila import test
|
||||
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers import nfs
|
||||
|
||||
|
||||
class DumbVolume(object):
|
||||
fields = {}
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.fields[key] = value
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.fields[item]
|
||||
|
||||
|
||||
class RemoteFsDriverTestCase(test.TestCase):
|
||||
TEST_EXPORT = '1.2.3.4/export1'
|
||||
TEST_FILE_NAME = 'test.txt'
|
||||
|
||||
def setUp(self):
|
||||
self._driver = nfs.RemoteFsDriver()
|
||||
self._mox = mox_lib.Mox()
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
self._mox.UnsetStubs()
|
||||
|
||||
def test_create_sparsed_file(self):
|
||||
(mox, drv) = self._mox, self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('truncate', '-s', '1G', '/path', run_as_root=True).\
|
||||
AndReturn("")
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._create_sparsed_file('/path', 1)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_regular_file(self):
|
||||
(mox, drv) = self._mox, self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('dd', 'if=/dev/zero', 'of=/path', 'bs=1M', 'count=1024',
|
||||
run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._create_regular_file('/path', 1)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_set_rw_permissions_for_all(self):
|
||||
(mox, drv) = self._mox, self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('chmod', 'ugo+rw', '/path', run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._set_rw_permissions_for_all('/path')
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_get_hash_str(self):
|
||||
"""_get_hash_str should calculation correct value."""
|
||||
drv = self._driver
|
||||
|
||||
self.assertEqual('4d664fd43b6ff86d80a4ea969c07b3b9',
|
||||
drv._get_hash_str(self.TEST_EXPORT))
|
||||
|
||||
|
||||
class NfsDriverTestCase(test.TestCase):
|
||||
"""Test case for NFS driver."""
|
||||
|
||||
TEST_NFS_EXPORT1 = 'nfs-host1:/export'
|
||||
TEST_NFS_EXPORT2 = 'nfs-host2:/export'
|
||||
TEST_SIZE_IN_GB = 1
|
||||
TEST_MNT_POINT = '/mnt/nfs'
|
||||
TEST_MNT_POINT_BASE = '/mnt/test'
|
||||
TEST_LOCAL_PATH = '/mnt/nfs/volume-123'
|
||||
TEST_FILE_NAME = 'test.txt'
|
||||
TEST_SHARES_CONFIG_FILE = '/etc/manila/test-shares.conf'
|
||||
ONE_GB_IN_BYTES = 1024 * 1024 * 1024
|
||||
|
||||
def setUp(self):
|
||||
self._mox = mox_lib.Mox()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.configuration = mox_lib.MockObject(conf.Configuration)
|
||||
self.configuration.append_config_values(mox_lib.IgnoreArg())
|
||||
self.configuration.nfs_shares_config = None
|
||||
self.configuration.nfs_mount_options = None
|
||||
self.configuration.nfs_mount_point_base = '$state_path/mnt'
|
||||
self.configuration.nfs_disk_util = 'df'
|
||||
self.configuration.nfs_sparsed_volumes = True
|
||||
self._driver = nfs.NfsDriver(configuration=self.configuration)
|
||||
|
||||
def tearDown(self):
|
||||
self._mox.UnsetStubs()
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def stub_out_not_replaying(self, obj, attr_name):
|
||||
attr_to_replace = getattr(obj, attr_name)
|
||||
stub = mox_lib.MockObject(attr_to_replace)
|
||||
self.stubs.Set(obj, attr_name, stub)
|
||||
|
||||
def test_local_path(self):
|
||||
"""local_path common use case."""
|
||||
self.configuration.nfs_mount_point_base = self.TEST_MNT_POINT_BASE
|
||||
drv = self._driver
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['provider_location'] = self.TEST_NFS_EXPORT1
|
||||
volume['name'] = 'volume-123'
|
||||
|
||||
self.assertEqual(
|
||||
'/mnt/test/2f4f60214cf43c595666dd815f0360a4/volume-123',
|
||||
drv.local_path(volume))
|
||||
|
||||
def test_mount_nfs_should_mount_correctly(self):
|
||||
"""_mount_nfs common case usage."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute('mount', '-t', 'nfs', self.TEST_NFS_EXPORT1,
|
||||
self.TEST_MNT_POINT, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_nfs_should_suppress_already_mounted_error(self):
|
||||
"""_mount_nfs should suppress already mounted error if ensure=True
|
||||
"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute('mount', '-t', 'nfs', self.TEST_NFS_EXPORT1,
|
||||
self.TEST_MNT_POINT, run_as_root=True).\
|
||||
AndRaise(ProcessExecutionError(
|
||||
stderr='is busy or already mounted'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, ensure=True)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_nfs_should_reraise_already_mounted_error(self):
|
||||
"""_mount_nfs should not suppress already mounted error if ensure=False
|
||||
"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute(
|
||||
'mount',
|
||||
'-t',
|
||||
'nfs',
|
||||
self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, run_as_root=True).\
|
||||
AndRaise(ProcessExecutionError(stderr='is busy or '
|
||||
'already mounted'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(ProcessExecutionError, drv._mount_nfs,
|
||||
self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT,
|
||||
ensure=False)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_nfs_should_create_mountpoint_if_not_yet(self):
|
||||
"""_mount_nfs should create mountpoint if it doesn't exist."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_mount_nfs_should_not_create_mountpoint_if_already(self):
|
||||
"""_mount_nfs should not create mountpoint if it already exists."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mkdir', '-p', self.TEST_MNT_POINT)
|
||||
drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_get_hash_str(self):
|
||||
"""_get_hash_str should calculation correct value."""
|
||||
drv = self._driver
|
||||
|
||||
self.assertEqual('2f4f60214cf43c595666dd815f0360a4',
|
||||
drv._get_hash_str(self.TEST_NFS_EXPORT1))
|
||||
|
||||
def test_get_mount_point_for_share(self):
|
||||
"""_get_mount_point_for_share should calculate correct value."""
|
||||
drv = self._driver
|
||||
|
||||
self.configuration.nfs_mount_point_base = self.TEST_MNT_POINT_BASE
|
||||
|
||||
self.assertEqual('/mnt/test/2f4f60214cf43c595666dd815f0360a4',
|
||||
drv._get_mount_point_for_share(self.TEST_NFS_EXPORT1))
|
||||
|
||||
def test_get_available_capacity_with_df(self):
|
||||
"""_get_available_capacity should calculate correct value."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
df_total_size = 2620544
|
||||
df_avail = 1490560
|
||||
df_head = 'Filesystem 1K-blocks Used Available Use% Mounted on\n'
|
||||
df_data = 'nfs-host:/export %d 996864 %d 41%% /mnt' % (df_total_size,
|
||||
df_avail)
|
||||
df_output = df_head + df_data
|
||||
|
||||
self.configuration.nfs_disk_util = 'df'
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('df', '-P', '-B', '1', self.TEST_MNT_POINT,
|
||||
run_as_root=True).AndReturn((df_output, None))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEquals((df_avail, df_total_size),
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT1))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_get_available_capacity_with_du(self):
|
||||
"""_get_available_capacity should calculate correct value."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
self.configuration.nfs_disk_util = 'du'
|
||||
|
||||
df_total_size = 2620544
|
||||
df_used_size = 996864
|
||||
df_avail_size = 1490560
|
||||
df_title = 'Filesystem 1-blocks Used Available Use% Mounted on\n'
|
||||
df_mnt_data = 'nfs-host:/export %d %d %d 41%% /mnt' % (df_total_size,
|
||||
df_used_size,
|
||||
df_avail_size)
|
||||
df_output = df_title + df_mnt_data
|
||||
|
||||
du_used = 490560
|
||||
du_output = '%d /mnt' % du_used
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('df', '-P', '-B', '1', self.TEST_MNT_POINT,
|
||||
run_as_root=True).\
|
||||
AndReturn((df_output, None))
|
||||
drv._execute('du', '-sb', '--apparent-size',
|
||||
'--exclude', '*snapshot*',
|
||||
self.TEST_MNT_POINT,
|
||||
run_as_root=True).AndReturn((du_output, None))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEquals((df_total_size - du_used, df_total_size),
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT1))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_load_shares_config(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.configuration.nfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
mox.StubOutWithMock(__builtin__, 'open')
|
||||
config_data = []
|
||||
config_data.append(self.TEST_NFS_EXPORT1)
|
||||
config_data.append('#' + self.TEST_NFS_EXPORT2)
|
||||
config_data.append('')
|
||||
__builtin__.open(self.TEST_SHARES_CONFIG_FILE).AndReturn(config_data)
|
||||
mox.ReplayAll()
|
||||
|
||||
shares = drv._load_shares_config()
|
||||
|
||||
self.assertEqual([self.TEST_NFS_EXPORT1], shares)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_share_mounted(self):
|
||||
"""_ensure_share_mounted simple use case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_mount_point_for_share')
|
||||
drv._get_mount_point_for_share(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn(self.TEST_MNT_POINT)
|
||||
|
||||
mox.StubOutWithMock(drv, '_mount_nfs')
|
||||
drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT, ensure=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_share_mounted(self.TEST_NFS_EXPORT1)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_shares_mounted_should_save_mounting_successfully(self):
|
||||
"""_ensure_shares_mounted should save share if mounted with success."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_load_shares_config')
|
||||
drv._load_shares_config().AndReturn([self.TEST_NFS_EXPORT1])
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_NFS_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
self.assertEqual(1, len(drv._mounted_shares))
|
||||
self.assertEqual(self.TEST_NFS_EXPORT1, drv._mounted_shares[0])
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_ensure_shares_mounted_should_not_save_mounting_with_error(self):
|
||||
"""_ensure_shares_mounted should not save share if failed to mount."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
mox.StubOutWithMock(drv, '_load_shares_config')
|
||||
drv._load_shares_config().AndReturn([self.TEST_NFS_EXPORT1])
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_NFS_EXPORT1).AndRaise(Exception())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
self.assertEqual(0, len(drv._mounted_shares))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_setup_should_throw_error_if_shares_config_not_configured(self):
|
||||
"""do_setup should throw error if shares config is not configured."""
|
||||
drv = self._driver
|
||||
self.configuration.nfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
self.assertRaises(exception.NfsException,
|
||||
drv.do_setup, IsA(context.RequestContext))
|
||||
|
||||
def test_setup_should_throw_exception_if_nfs_client_is_not_installed(self):
|
||||
"""do_setup should throw error if nfs client is not installed."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
self.configuration.nfs_shares_config = self.TEST_SHARES_CONFIG_FILE
|
||||
|
||||
mox.StubOutWithMock(os.path, 'exists')
|
||||
os.path.exists(self.TEST_SHARES_CONFIG_FILE).AndReturn(True)
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('mount.nfs', check_exit_code=False).\
|
||||
AndRaise(OSError(errno.ENOENT, 'No such file or directory'))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exception.NfsException,
|
||||
drv.do_setup, IsA(context.RequestContext))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_find_share_should_throw_error_if_there_is_no_mounted_shares(self):
|
||||
"""_find_share should throw error if there is no mounted shares."""
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = []
|
||||
|
||||
self.assertRaises(exception.NotFound, drv._find_share,
|
||||
self.TEST_SIZE_IN_GB)
|
||||
|
||||
def test_find_share(self):
|
||||
"""_find_share simple use case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = [self.TEST_NFS_EXPORT1, self.TEST_NFS_EXPORT2]
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_available_capacity')
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn((2 * self.ONE_GB_IN_BYTES, 5 * self.ONE_GB_IN_BYTES))
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT2).\
|
||||
AndReturn((3 * self.ONE_GB_IN_BYTES, 10 * self.ONE_GB_IN_BYTES))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertEqual(self.TEST_NFS_EXPORT2,
|
||||
drv._find_share(self.TEST_SIZE_IN_GB))
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_find_share_should_throw_error_if_there_is_no_enough_place(self):
|
||||
"""_find_share should throw error if there is no share to host vol."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = [self.TEST_NFS_EXPORT1, self.TEST_NFS_EXPORT2]
|
||||
|
||||
mox.StubOutWithMock(drv, '_get_available_capacity')
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn((0, 5 * self.ONE_GB_IN_BYTES))
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT2).\
|
||||
AndReturn((0, 10 * self.ONE_GB_IN_BYTES))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exception.NfsNoSuitableShareFound, drv._find_share,
|
||||
self.TEST_SIZE_IN_GB)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def _simple_volume(self):
|
||||
volume = DumbVolume()
|
||||
volume['provider_location'] = '127.0.0.1:/mnt'
|
||||
volume['name'] = 'volume_name'
|
||||
volume['size'] = 10
|
||||
|
||||
return volume
|
||||
|
||||
def test_create_sparsed_volume(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
volume = self._simple_volume()
|
||||
|
||||
setattr(cfg.CONF, 'nfs_sparsed_volumes', True)
|
||||
|
||||
mox.StubOutWithMock(drv, '_create_sparsed_file')
|
||||
mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
|
||||
|
||||
drv._create_sparsed_file(IgnoreArg(), IgnoreArg())
|
||||
drv._set_rw_permissions_for_all(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._do_create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
delattr(cfg.CONF, 'nfs_sparsed_volumes')
|
||||
|
||||
def test_create_nonsparsed_volume(self):
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
self.configuration.nfs_sparsed_volumes = False
|
||||
volume = self._simple_volume()
|
||||
|
||||
setattr(cfg.CONF, 'nfs_sparsed_volumes', False)
|
||||
|
||||
mox.StubOutWithMock(drv, '_create_regular_file')
|
||||
mox.StubOutWithMock(drv, '_set_rw_permissions_for_all')
|
||||
|
||||
drv._create_regular_file(IgnoreArg(), IgnoreArg())
|
||||
drv._set_rw_permissions_for_all(IgnoreArg())
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv._do_create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
delattr(cfg.CONF, 'nfs_sparsed_volumes')
|
||||
|
||||
def test_create_volume_should_ensure_nfs_mounted(self):
|
||||
"""create_volume ensures shares provided in config are mounted."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(nfs, 'LOG')
|
||||
self.stub_out_not_replaying(drv, '_find_share')
|
||||
self.stub_out_not_replaying(drv, '_do_create_volume')
|
||||
|
||||
mox.StubOutWithMock(drv, '_ensure_shares_mounted')
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['size'] = self.TEST_SIZE_IN_GB
|
||||
drv.create_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_create_volume_should_return_provider_location(self):
|
||||
"""create_volume should return provider_location with found share."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(nfs, 'LOG')
|
||||
self.stub_out_not_replaying(drv, '_ensure_shares_mounted')
|
||||
self.stub_out_not_replaying(drv, '_do_create_volume')
|
||||
|
||||
mox.StubOutWithMock(drv, '_find_share')
|
||||
drv._find_share(self.TEST_SIZE_IN_GB).AndReturn(self.TEST_NFS_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['size'] = self.TEST_SIZE_IN_GB
|
||||
result = drv.create_volume(volume)
|
||||
self.assertEqual(self.TEST_NFS_EXPORT1, result['provider_location'])
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_volume(self):
|
||||
"""delete_volume simple test case."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_ensure_share_mounted')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = self.TEST_NFS_EXPORT1
|
||||
|
||||
mox.StubOutWithMock(drv, 'local_path')
|
||||
drv.local_path(volume).AndReturn(self.TEST_LOCAL_PATH)
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
drv._execute('rm', '-f', self.TEST_LOCAL_PATH, run_as_root=True)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_should_ensure_share_mounted(self):
|
||||
"""delete_volume should ensure that corresponding share is mounted."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_execute')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = self.TEST_NFS_EXPORT1
|
||||
|
||||
mox.StubOutWithMock(drv, '_ensure_share_mounted')
|
||||
drv._ensure_share_mounted(self.TEST_NFS_EXPORT1)
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_delete_should_not_delete_if_provider_location_not_provided(self):
|
||||
"""delete_volume shouldn't delete if provider_location missed."""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
self.stub_out_not_replaying(drv, '_ensure_share_mounted')
|
||||
|
||||
volume = DumbVolume()
|
||||
volume['name'] = 'volume-123'
|
||||
volume['provider_location'] = None
|
||||
|
||||
mox.StubOutWithMock(drv, '_execute')
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.delete_volume(volume)
|
||||
|
||||
mox.VerifyAll()
|
||||
|
||||
def test_get_volume_stats(self):
|
||||
"""get_volume_stats must fill the correct values"""
|
||||
mox = self._mox
|
||||
drv = self._driver
|
||||
|
||||
drv._mounted_shares = [self.TEST_NFS_EXPORT1, self.TEST_NFS_EXPORT2]
|
||||
|
||||
mox.StubOutWithMock(drv, '_ensure_shares_mounted')
|
||||
mox.StubOutWithMock(drv, '_get_available_capacity')
|
||||
|
||||
drv._ensure_shares_mounted()
|
||||
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT1).\
|
||||
AndReturn((2 * self.ONE_GB_IN_BYTES, 10 * self.ONE_GB_IN_BYTES))
|
||||
drv._get_available_capacity(self.TEST_NFS_EXPORT2).\
|
||||
AndReturn((3 * self.ONE_GB_IN_BYTES, 20 * self.ONE_GB_IN_BYTES))
|
||||
|
||||
mox.ReplayAll()
|
||||
|
||||
drv.get_volume_stats()
|
||||
self.assertEqual(drv._stats['total_capacity_gb'], 30.0)
|
||||
self.assertEqual(drv._stats['free_capacity_gb'], 5.0)
|
||||
|
||||
mox.VerifyAll()
|
|
@ -29,7 +29,6 @@ from manila.openstack.common import timeutils
|
|||
from manila import quota
|
||||
from manila import test
|
||||
import manila.tests.image.fake
|
||||
from manila import volume
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Josh Durgin
|
||||
# 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.
|
||||
|
||||
import contextlib
|
||||
import mox
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.image import image_utils
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common import timeutils
|
||||
from manila import test
|
||||
from manila.tests.image import fake as fake_image
|
||||
from manila.tests.test_volume import DriverTestCase
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.rbd import RBDDriver
|
||||
from manila.volume.drivers.rbd import VERSION as DRIVER_VERSION
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeImageService:
|
||||
def download(self, context, image_id, path):
|
||||
pass
|
||||
|
||||
RADOS_DF_OUT = """
|
||||
{
|
||||
"total_space" : "958931232",
|
||||
"total_used" : "123906196",
|
||||
"total_objects" : "4221",
|
||||
"total_avail" : "787024012",
|
||||
"pools" : [
|
||||
{
|
||||
"name" : "volumes",
|
||||
"categories" : [
|
||||
{
|
||||
"write_bytes" : "226833",
|
||||
"size_kb" : "17038386",
|
||||
"read_bytes" : "221865",
|
||||
"num_objects" : "4186",
|
||||
"name" : "",
|
||||
"size_bytes" : "17447306589",
|
||||
"write_kb" : "20302730",
|
||||
"num_object_copies" : "8372",
|
||||
"read_kb" : "30",
|
||||
"num_objects_unfound" : "0",
|
||||
"num_object_clones" : "9",
|
||||
"num_objects_missing_on_primary" : "0",
|
||||
"num_objects_degraded" : "0"
|
||||
}
|
||||
],
|
||||
"id" : "4"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class RBDTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RBDTestCase, self).setUp()
|
||||
|
||||
def fake_execute(*args, **kwargs):
|
||||
return '', ''
|
||||
self._mox = mox.Mox()
|
||||
self.configuration = mox.MockObject(conf.Configuration)
|
||||
self.configuration.volume_tmp_dir = None
|
||||
self.configuration.rbd_pool = 'rbd'
|
||||
self.configuration.rbd_secret_uuid = None
|
||||
self.configuration.rbd_user = None
|
||||
self.configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
self.driver = RBDDriver(execute=fake_execute,
|
||||
configuration=self.configuration)
|
||||
self._mox.ReplayAll()
|
||||
|
||||
def test_good_locations(self):
|
||||
locations = ['rbd://fsid/pool/image/snap',
|
||||
'rbd://%2F/%2F/%2F/%2F', ]
|
||||
map(self.driver._parse_location, locations)
|
||||
|
||||
def test_bad_locations(self):
|
||||
locations = ['rbd://image',
|
||||
'http://path/to/somewhere/else',
|
||||
'rbd://image/extra',
|
||||
'rbd://image/',
|
||||
'rbd://fsid/pool/image/',
|
||||
'rbd://fsid/pool/image/snap/',
|
||||
'rbd://///', ]
|
||||
for loc in locations:
|
||||
self.assertRaises(exception.ImageUnacceptable,
|
||||
self.driver._parse_location,
|
||||
loc)
|
||||
self.assertFalse(self.driver._is_cloneable(loc))
|
||||
|
||||
def test_cloneable(self):
|
||||
self.stubs.Set(self.driver, '_get_fsid', lambda: 'abc')
|
||||
location = 'rbd://abc/pool/image/snap'
|
||||
self.assertTrue(self.driver._is_cloneable(location))
|
||||
|
||||
def test_uncloneable_different_fsid(self):
|
||||
self.stubs.Set(self.driver, '_get_fsid', lambda: 'abc')
|
||||
location = 'rbd://def/pool/image/snap'
|
||||
self.assertFalse(self.driver._is_cloneable(location))
|
||||
|
||||
def test_uncloneable_unreadable(self):
|
||||
def fake_exc(*args):
|
||||
raise exception.ProcessExecutionError()
|
||||
self.stubs.Set(self.driver, '_get_fsid', lambda: 'abc')
|
||||
self.stubs.Set(self.driver, '_execute', fake_exc)
|
||||
location = 'rbd://abc/pool/image/snap'
|
||||
self.assertFalse(self.driver._is_cloneable(location))
|
||||
|
||||
def _copy_image(self):
|
||||
@contextlib.contextmanager
|
||||
def fake_temp_file(dir):
|
||||
class FakeTmp:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
yield FakeTmp('test')
|
||||
self.stubs.Set(tempfile, 'NamedTemporaryFile', fake_temp_file)
|
||||
self.stubs.Set(os.path, 'exists', lambda x: True)
|
||||
self.stubs.Set(image_utils, 'fetch_to_raw', lambda w, x, y, z: None)
|
||||
self.driver.copy_image_to_volume(None, {'name': 'test',
|
||||
'size': 1},
|
||||
FakeImageService(), None)
|
||||
|
||||
def test_copy_image_no_volume_tmp(self):
|
||||
self.configuration.volume_tmp_dir = None
|
||||
self._copy_image()
|
||||
|
||||
def test_copy_image_volume_tmp(self):
|
||||
self.configuration.volume_tmp_dir = '/var/run/manila/tmp'
|
||||
self._copy_image()
|
||||
|
||||
def test_update_volume_stats(self):
|
||||
def fake_stats(*args):
|
||||
return RADOS_DF_OUT, ''
|
||||
|
||||
def fake_safe_get(*args):
|
||||
return "RBD"
|
||||
|
||||
self.stubs.Set(self.driver, '_execute', fake_stats)
|
||||
self.stubs.Set(self.driver.configuration, 'safe_get', fake_safe_get)
|
||||
expected = dict(
|
||||
volume_backend_name='RBD',
|
||||
vendor_name='Open Source',
|
||||
driver_version=DRIVER_VERSION,
|
||||
storage_protocol='ceph',
|
||||
total_capacity_gb=914,
|
||||
free_capacity_gb=750,
|
||||
reserved_percentage=0)
|
||||
actual = self.driver.get_volume_stats(True)
|
||||
self.assertDictMatch(expected, actual)
|
||||
|
||||
def test_update_volume_stats_error(self):
|
||||
def fake_exc(*args):
|
||||
raise exception.ProcessExecutionError()
|
||||
|
||||
def fake_safe_get(*args):
|
||||
return "RBD"
|
||||
|
||||
self.stubs.Set(self.driver, '_execute', fake_exc)
|
||||
self.stubs.Set(self.driver.configuration, 'safe_get', fake_safe_get)
|
||||
expected = dict(
|
||||
volume_backend_name='RBD',
|
||||
vendor_name='Open Source',
|
||||
driver_version=DRIVER_VERSION,
|
||||
storage_protocol='ceph',
|
||||
total_capacity_gb='unknown',
|
||||
free_capacity_gb='unknown',
|
||||
reserved_percentage=0)
|
||||
actual = self.driver.get_volume_stats(True)
|
||||
self.assertDictMatch(expected, actual)
|
||||
|
||||
|
||||
class ManagedRBDTestCase(DriverTestCase):
|
||||
driver_name = "manila.volume.drivers.rbd.RBDDriver"
|
||||
|
||||
def setUp(self):
|
||||
super(ManagedRBDTestCase, self).setUp()
|
||||
fake_image.stub_out_image_service(self.stubs)
|
||||
|
||||
def _clone_volume_from_image(self, expected_status,
|
||||
clone_works=True):
|
||||
"""Try to clone a volume from an image, and check the status
|
||||
afterwards"""
|
||||
def fake_clone_image(volume, image_location):
|
||||
return True
|
||||
|
||||
def fake_clone_error(volume, image_location):
|
||||
raise exception.ManilaException()
|
||||
|
||||
self.stubs.Set(self.volume.driver, '_is_cloneable', lambda x: True)
|
||||
if clone_works:
|
||||
self.stubs.Set(self.volume.driver, 'clone_image', fake_clone_image)
|
||||
else:
|
||||
self.stubs.Set(self.volume.driver, 'clone_image', fake_clone_error)
|
||||
|
||||
image_id = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
|
||||
volume_id = 1
|
||||
# creating volume testdata
|
||||
db.volume_create(self.context,
|
||||
{'id': volume_id,
|
||||
'updated_at': timeutils.utcnow(),
|
||||
'display_description': 'Test Desc',
|
||||
'size': 20,
|
||||
'status': 'creating',
|
||||
'instance_uuid': None,
|
||||
'host': 'dummy'})
|
||||
try:
|
||||
if clone_works:
|
||||
self.volume.create_volume(self.context,
|
||||
volume_id,
|
||||
image_id=image_id)
|
||||
else:
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.volume.create_volume,
|
||||
self.context,
|
||||
volume_id,
|
||||
image_id=image_id)
|
||||
|
||||
volume = db.volume_get(self.context, volume_id)
|
||||
self.assertEqual(volume['status'], expected_status)
|
||||
finally:
|
||||
# cleanup
|
||||
db.volume_destroy(self.context, volume_id)
|
||||
|
||||
def test_clone_image_status_available(self):
|
||||
"""Verify that before cloning, an image is in the available state."""
|
||||
self._clone_volume_from_image('available', True)
|
||||
|
||||
def test_clone_image_status_error(self):
|
||||
"""Verify that before cloning, an image is in the available state."""
|
||||
self._clone_volume_from_image('error', False)
|
||||
|
||||
def test_clone_success(self):
|
||||
self.stubs.Set(self.volume.driver, '_is_cloneable', lambda x: True)
|
||||
self.stubs.Set(self.volume.driver, 'clone_image', lambda a, b: True)
|
||||
image_id = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
|
||||
self.assertTrue(self.volume.driver.clone_image({}, image_id))
|
||||
|
||||
def test_clone_bad_image_id(self):
|
||||
self.stubs.Set(self.volume.driver, '_is_cloneable', lambda x: True)
|
||||
self.assertFalse(self.volume.driver.clone_image({}, None))
|
||||
|
||||
def test_clone_uncloneable(self):
|
||||
self.stubs.Set(self.volume.driver, '_is_cloneable', lambda x: False)
|
||||
self.assertFalse(self.volume.driver.clone_image({}, 'dne'))
|
|
@ -1,185 +0,0 @@
|
|||
# Copyright (c) 2013 Scality
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Unit tests for the Scality SOFS Volume Driver.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import os
|
||||
|
||||
from manila import exception
|
||||
from manila import test
|
||||
from manila import utils
|
||||
from manila.volume.drivers import scality
|
||||
|
||||
|
||||
class ScalityDriverTestCase(test.TestCase):
|
||||
"""Test case for the Scality driver."""
|
||||
|
||||
TEST_MOUNT = '/tmp/fake_mount'
|
||||
TEST_CONFIG = '/tmp/fake_config'
|
||||
TEST_VOLDIR = 'volumes'
|
||||
|
||||
TEST_VOLNAME = 'volume_name'
|
||||
TEST_VOLSIZE = '0'
|
||||
TEST_VOLUME = {
|
||||
'name': TEST_VOLNAME,
|
||||
'size': TEST_VOLSIZE
|
||||
}
|
||||
TEST_VOLPATH = os.path.join(TEST_MOUNT,
|
||||
TEST_VOLDIR,
|
||||
TEST_VOLNAME)
|
||||
|
||||
TEST_SNAPNAME = 'snapshot_name'
|
||||
TEST_SNAPSHOT = {
|
||||
'name': TEST_SNAPNAME,
|
||||
'volume_name': TEST_VOLNAME,
|
||||
'volume_size': TEST_VOLSIZE
|
||||
}
|
||||
TEST_SNAPPATH = os.path.join(TEST_MOUNT,
|
||||
TEST_VOLDIR,
|
||||
TEST_SNAPNAME)
|
||||
|
||||
def _makedirs(self, path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise e
|
||||
|
||||
def _create_fake_config(self):
|
||||
open(self.TEST_CONFIG, "w+").close()
|
||||
|
||||
def _create_fake_mount(self):
|
||||
self._makedirs(os.path.join(self.TEST_MOUNT, 'sys'))
|
||||
self._makedirs(os.path.join(self.TEST_MOUNT, self.TEST_VOLDIR))
|
||||
|
||||
def _remove_fake_mount(self):
|
||||
utils.execute('rm', '-rf', self.TEST_MOUNT)
|
||||
|
||||
def _remove_fake_config(self):
|
||||
try:
|
||||
os.unlink(self.TEST_CONFIG)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise e
|
||||
|
||||
def _configure_driver(self):
|
||||
scality.FLAGS.scality_sofs_config = self.TEST_CONFIG
|
||||
scality.FLAGS.scality_sofs_mount_point = self.TEST_MOUNT
|
||||
scality.FLAGS.scality_sofs_volume_dir = self.TEST_VOLDIR
|
||||
|
||||
def _execute_wrapper(self, cmd, *args, **kwargs):
|
||||
try:
|
||||
kwargs.pop('run_as_root')
|
||||
except KeyError:
|
||||
pass
|
||||
utils.execute(cmd, *args, **kwargs)
|
||||
|
||||
def _set_access_wrapper(self, is_visible):
|
||||
|
||||
def _access_wrapper(path, flags):
|
||||
if path == '/sbin/mount.sofs':
|
||||
return is_visible
|
||||
else:
|
||||
return os.access(path, flags)
|
||||
|
||||
self.stubs.Set(os, 'access', _access_wrapper)
|
||||
|
||||
def setUp(self):
|
||||
super(ScalityDriverTestCase, self).setUp()
|
||||
|
||||
self._remove_fake_mount()
|
||||
self._driver = scality.ScalityDriver()
|
||||
self._driver.set_execute(self._execute_wrapper)
|
||||
|
||||
self._create_fake_mount()
|
||||
self._create_fake_config()
|
||||
self._configure_driver()
|
||||
|
||||
def tearDown(self):
|
||||
self._remove_fake_mount()
|
||||
self._remove_fake_config()
|
||||
super(ScalityDriverTestCase, self).tearDown()
|
||||
|
||||
def test_setup_no_config(self):
|
||||
"""Missing SOFS configuration shall raise an error."""
|
||||
scality.FLAGS.scality_sofs_config = None
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self._driver.do_setup, None)
|
||||
|
||||
def test_setup_missing_config(self):
|
||||
"""Non-existent SOFS configuration file shall raise an error."""
|
||||
scality.FLAGS.scality_sofs_config = 'nonexistent.conf'
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self._driver.do_setup, None)
|
||||
|
||||
def test_setup_no_mount_helper(self):
|
||||
"""SOFS must be installed to use the driver."""
|
||||
self._set_access_wrapper(False)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self._driver.do_setup, None)
|
||||
|
||||
def test_setup_make_voldir(self):
|
||||
"""The directory for volumes shall be created automatically."""
|
||||
self._set_access_wrapper(True)
|
||||
voldir_path = os.path.join(self.TEST_MOUNT, self.TEST_VOLDIR)
|
||||
os.rmdir(voldir_path)
|
||||
self._driver.do_setup(None)
|
||||
self.assertTrue(os.path.isdir(voldir_path))
|
||||
|
||||
def test_local_path(self):
|
||||
"""Expected behaviour for local_path."""
|
||||
self.assertEqual(self._driver.local_path(self.TEST_VOLUME),
|
||||
self.TEST_VOLPATH)
|
||||
|
||||
def test_create_volume(self):
|
||||
"""Expected behaviour for create_volume."""
|
||||
ret = self._driver.create_volume(self.TEST_VOLUME)
|
||||
self.assertEqual(ret['provider_location'],
|
||||
os.path.join(self.TEST_VOLDIR,
|
||||
self.TEST_VOLNAME))
|
||||
self.assertTrue(os.path.isfile(self.TEST_VOLPATH))
|
||||
self.assertEqual(os.stat(self.TEST_VOLPATH).st_size,
|
||||
100 * 1024 * 1024)
|
||||
|
||||
def test_delete_volume(self):
|
||||
"""Expected behaviour for delete_volume."""
|
||||
self._driver.create_volume(self.TEST_VOLUME)
|
||||
self._driver.delete_volume(self.TEST_VOLUME)
|
||||
self.assertFalse(os.path.isfile(self.TEST_VOLPATH))
|
||||
|
||||
def test_create_snapshot(self):
|
||||
"""Expected behaviour for create_snapshot."""
|
||||
self._driver.create_volume(self.TEST_VOLUME)
|
||||
self._driver.create_snapshot(self.TEST_SNAPSHOT)
|
||||
self.assertTrue(os.path.isfile(self.TEST_SNAPPATH))
|
||||
self.assertEqual(os.stat(self.TEST_SNAPPATH).st_size,
|
||||
100 * 1024 * 1024)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
"""Expected behaviour for delete_snapshot."""
|
||||
self._driver.create_volume(self.TEST_VOLUME)
|
||||
self._driver.create_snapshot(self.TEST_SNAPSHOT)
|
||||
self._driver.delete_snapshot(self.TEST_SNAPSHOT)
|
||||
self.assertFalse(os.path.isfile(self.TEST_SNAPPATH))
|
||||
|
||||
def test_initialize_connection(self):
|
||||
"""Expected behaviour for initialize_connection."""
|
||||
ret = self._driver.initialize_connection(self.TEST_VOLUME, None)
|
||||
self.assertEqual(ret['driver_volume_type'], 'scality')
|
||||
self.assertEqual(ret['data']['sofs_path'],
|
||||
os.path.join(self.TEST_VOLDIR,
|
||||
self.TEST_VOLNAME))
|
|
@ -1,64 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Wenhao Xu
|
||||
# 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 manila import exception
|
||||
from manila import test
|
||||
from manila.volume.drivers.sheepdog import SheepdogDriver
|
||||
|
||||
COLLIE_NODE_INFO = """
|
||||
0 107287605248 3623897354 3%
|
||||
Total 107287605248 3623897354 3% 54760833024
|
||||
"""
|
||||
|
||||
|
||||
class SheepdogTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SheepdogTestCase, self).setUp()
|
||||
self.driver = SheepdogDriver()
|
||||
|
||||
def test_update_volume_stats(self):
|
||||
def fake_stats(*args):
|
||||
return COLLIE_NODE_INFO, ''
|
||||
self.stubs.Set(self.driver, '_execute', fake_stats)
|
||||
expected = dict(
|
||||
volume_backend_name='sheepdog',
|
||||
vendor_name='Open Source',
|
||||
dirver_version='1.0',
|
||||
storage_protocol='sheepdog',
|
||||
total_capacity_gb=float(107287605248) / (1024 ** 3),
|
||||
free_capacity_gb=float(107287605248 - 3623897354) / (1024 ** 3),
|
||||
reserved_percentage=0,
|
||||
QoS_support=False)
|
||||
actual = self.driver.get_volume_stats(True)
|
||||
self.assertDictMatch(expected, actual)
|
||||
|
||||
def test_update_volume_stats_error(self):
|
||||
def fake_stats(*args):
|
||||
raise exception.ProcessExecutionError()
|
||||
self.stubs.Set(self.driver, '_execute', fake_stats)
|
||||
expected = dict(
|
||||
volume_backend_name='sheepdog',
|
||||
vendor_name='Open Source',
|
||||
dirver_version='1.0',
|
||||
storage_protocol='sheepdog',
|
||||
total_capacity_gb='unknown',
|
||||
free_capacity_gb='unknown',
|
||||
reserved_percentage=0,
|
||||
QoS_support=False)
|
||||
actual = self.driver.get_volume_stats(True)
|
||||
self.assertDictMatch(expected, actual)
|
|
@ -1,283 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import mox
|
||||
from mox import IgnoreArg
|
||||
from mox import IsA
|
||||
from mox import stubout
|
||||
|
||||
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers.solidfire import SolidFire
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_configuration():
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.san_is_local = False
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
return configuration
|
||||
|
||||
|
||||
class SolidFireVolumeTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
self._mox = mox.Mox()
|
||||
self.configuration = mox.MockObject(conf.Configuration)
|
||||
self.configuration.sf_allow_tenant_qos = True
|
||||
self.configuration.san_is_local = True
|
||||
self.configuration.sf_emulate_512 = True
|
||||
self.configuration.sf_account_prefix = 'manila'
|
||||
|
||||
super(SolidFireVolumeTestCase, self).setUp()
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
|
||||
def fake_issue_api_request(obj, method, params):
|
||||
if method is 'GetClusterCapacity':
|
||||
LOG.info('Called Fake GetClusterCapacity...')
|
||||
data = {}
|
||||
data = {'result':
|
||||
{'clusterCapacity': {'maxProvisionedSpace': 99999999,
|
||||
'usedSpace': 999,
|
||||
'compressionPercent': 100,
|
||||
'deDuplicationPercent': 100,
|
||||
'thinProvisioningPercent': 100}}}
|
||||
return data
|
||||
|
||||
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':
|
||||
test_name = 'OS-VOLID-a720b3c0-d1f0-11e1-9b23-0800200c9a66'
|
||||
LOG.info('Called Fake ListVolumesForAccount...')
|
||||
result = {'result': {
|
||||
'volumes': [{'volumeID': 5,
|
||||
'name': test_name,
|
||||
'accountID': 25,
|
||||
'sliceCount': 1,
|
||||
'totalSize': 1048576 * 1024,
|
||||
'enable512e': True,
|
||||
'access': "readWrite",
|
||||
'status': "active",
|
||||
'attributes': None,
|
||||
'qos': None,
|
||||
'iqn': test_name}]}}
|
||||
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 fake_set_qos_by_volume_type(self, type_id, ctxt):
|
||||
return {'minIOPS': 500,
|
||||
'maxIOPS': 1000,
|
||||
'burstIOPS': 1000}
|
||||
|
||||
def fake_volume_get(obj, key, default=None):
|
||||
return {'qos': 'fast'}
|
||||
|
||||
def fake_update_cluster_status(self):
|
||||
return
|
||||
|
||||
def test_create_with_qos_type(self):
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
self.stubs.Set(SolidFire, '_set_qos_by_volume_type',
|
||||
self.fake_set_qos_by_volume_type)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'testvol',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||
'volume_type_id': 'fast'}
|
||||
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
model_update = sfv.create_volume(testvol)
|
||||
self.assertNotEqual(model_update, None)
|
||||
|
||||
def test_create_volume(self):
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'testvol',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
|
||||
'volume_type_id': None}
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
model_update = sfv.create_volume(testvol)
|
||||
self.assertNotEqual(model_update, None)
|
||||
|
||||
def test_create_volume_with_qos(self):
|
||||
preset_qos = {}
|
||||
preset_qos['qos'] = 'fast'
|
||||
self.stubs.Set(SolidFire, '_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}
|
||||
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
model_update = sfv.create_volume(testvol)
|
||||
self.assertNotEqual(model_update, None)
|
||||
|
||||
def test_create_volume_fails(self):
|
||||
# NOTE(JDG) This test just fakes update_cluster_status
|
||||
# this is inentional for this test
|
||||
self.stubs.Set(SolidFire, '_update_cluster_status',
|
||||
self.fake_update_cluster_status)
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request_fails)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'testvol',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
try:
|
||||
sfv.create_volume(testvol)
|
||||
self.fail("Should have thrown Error")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_sfaccount(self):
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
self.stubs.Set(SolidFire, '_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 = SolidFire(configuration=self.configuration)
|
||||
self.stubs.Set(SolidFire, '_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 = SolidFire(configuration=self.configuration)
|
||||
self.stubs.Set(SolidFire, '_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 = SolidFire(configuration=self.configuration)
|
||||
self.stubs.Set(SolidFire, '_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):
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'test_volume',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
sfv.delete_volume(testvol)
|
||||
|
||||
def test_delete_volume_fails_no_volume(self):
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'no-name',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
try:
|
||||
sfv.delete_volume(testvol)
|
||||
self.fail("Should have thrown Error")
|
||||
except Exception:
|
||||
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, '_update_cluster_status',
|
||||
self.fake_update_cluster_status)
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request_fails)
|
||||
testvol = {'project_id': 'testprjid',
|
||||
'name': 'no-name',
|
||||
'size': 1,
|
||||
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
self.assertRaises(exception.SfAccountNotFound,
|
||||
sfv.delete_volume,
|
||||
testvol)
|
||||
|
||||
def test_get_cluster_info(self):
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request)
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
sfv._get_cluster_info()
|
||||
|
||||
def test_get_cluster_info_fail(self):
|
||||
# NOTE(JDG) This test just fakes update_cluster_status
|
||||
# this is inentional for this test
|
||||
self.stubs.Set(SolidFire, '_update_cluster_status',
|
||||
self.fake_update_cluster_status)
|
||||
self.stubs.Set(SolidFire, '_issue_api_request',
|
||||
self.fake_issue_api_request_fails)
|
||||
sfv = SolidFire(configuration=self.configuration)
|
||||
self.assertRaises(exception.SolidFireAPIException,
|
||||
sfv._get_cluster_info)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,72 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (c) 2012 Rackspace Hosting
|
||||
# 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.
|
||||
|
||||
"""Tests for the configuration wrapper in volume drivers."""
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume import configuration
|
||||
from manila.volume import driver
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
volume_opts = [
|
||||
cfg.StrOpt('str_opt', default='STR_OPT'),
|
||||
cfg.BoolOpt('bool_opt', default=False)
|
||||
]
|
||||
more_volume_opts = [
|
||||
cfg.IntOpt('int_opt', default=1),
|
||||
]
|
||||
|
||||
FLAGS.register_opts(volume_opts)
|
||||
FLAGS.register_opts(more_volume_opts)
|
||||
|
||||
|
||||
class VolumeConfigurationTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VolumeConfigurationTest, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
super(VolumeConfigurationTest, self).tearDown()
|
||||
|
||||
def test_group_grafts_opts(self):
|
||||
c = configuration.Configuration(volume_opts, config_group='foo')
|
||||
self.assertEquals(c.str_opt, FLAGS.foo.str_opt)
|
||||
self.assertEquals(c.bool_opt, FLAGS.foo.bool_opt)
|
||||
|
||||
def test_opts_no_group(self):
|
||||
c = configuration.Configuration(volume_opts)
|
||||
self.assertEquals(c.str_opt, FLAGS.str_opt)
|
||||
self.assertEquals(c.bool_opt, FLAGS.bool_opt)
|
||||
|
||||
def test_grafting_multiple_opts(self):
|
||||
c = configuration.Configuration(volume_opts, config_group='foo')
|
||||
c.append_config_values(more_volume_opts)
|
||||
self.assertEquals(c.str_opt, FLAGS.foo.str_opt)
|
||||
self.assertEquals(c.bool_opt, FLAGS.foo.bool_opt)
|
||||
self.assertEquals(c.int_opt, FLAGS.foo.int_opt)
|
||||
|
||||
def test_safe_get(self):
|
||||
c = configuration.Configuration(volume_opts, config_group='foo')
|
||||
self.assertEquals(c.safe_get('none_opt'), None)
|
|
@ -1,131 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Zadara Storage Inc.
|
||||
# Copyright (c) 2011 OpenStack LLC.
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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.
|
||||
"""
|
||||
Unit Tests for volume types extra specs code
|
||||
"""
|
||||
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import test
|
||||
|
||||
|
||||
class VolumeGlanceMetadataTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeGlanceMetadataTestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
super(VolumeGlanceMetadataTestCase, self).tearDown()
|
||||
|
||||
def test_vol_glance_metadata_bad_vol_id(self):
|
||||
ctxt = context.get_admin_context()
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
db.volume_glance_metadata_create,
|
||||
ctxt, 1, 'key1', 'value1')
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
db.volume_glance_metadata_get, ctxt, 1)
|
||||
db.volume_glance_metadata_delete_by_volume(ctxt, 10)
|
||||
|
||||
def test_vol_update_glance_metadata(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db.volume_create(ctxt, {'id': 1})
|
||||
db.volume_create(ctxt, {'id': 2})
|
||||
vol_metadata = db.volume_glance_metadata_create(ctxt, 1, 'key1',
|
||||
'value1')
|
||||
vol_metadata = db.volume_glance_metadata_create(ctxt, 2, 'key1',
|
||||
'value1')
|
||||
vol_metadata = db.volume_glance_metadata_create(ctxt, 2,
|
||||
'key2',
|
||||
'value2')
|
||||
|
||||
expected_metadata_1 = {'volume_id': '1',
|
||||
'key': 'key1',
|
||||
'value': 'value1'}
|
||||
|
||||
metadata = db.volume_glance_metadata_get(ctxt, 1)
|
||||
self.assertEqual(len(metadata), 1)
|
||||
for key, value in expected_metadata_1.items():
|
||||
self.assertEqual(metadata[0][key], value)
|
||||
|
||||
expected_metadata_2 = ({'volume_id': '2',
|
||||
'key': 'key1',
|
||||
'value': 'value1'},
|
||||
{'volume_id': '2',
|
||||
'key': 'key2',
|
||||
'value': 'value2'})
|
||||
|
||||
metadata = db.volume_glance_metadata_get(ctxt, 2)
|
||||
self.assertEqual(len(metadata), 2)
|
||||
for expected, meta in zip(expected_metadata_2, metadata):
|
||||
for key, value in expected.iteritems():
|
||||
self.assertEqual(meta[key], value)
|
||||
|
||||
self.assertRaises(exception.GlanceMetadataExists,
|
||||
db.volume_glance_metadata_create,
|
||||
ctxt, 1, 'key1', 'value1a')
|
||||
|
||||
metadata = db.volume_glance_metadata_get(ctxt, 1)
|
||||
self.assertEqual(len(metadata), 1)
|
||||
for key, value in expected_metadata_1.items():
|
||||
self.assertEqual(metadata[0][key], value)
|
||||
|
||||
def test_vol_delete_glance_metadata(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db.volume_create(ctxt, {'id': 1})
|
||||
db.volume_glance_metadata_delete_by_volume(ctxt, 1)
|
||||
vol_metadata = db.volume_glance_metadata_create(ctxt, 1, 'key1',
|
||||
'value1')
|
||||
db.volume_glance_metadata_delete_by_volume(ctxt, 1)
|
||||
metadata = db.volume_glance_metadata_get(ctxt, 1)
|
||||
self.assertEqual(len(metadata), 0)
|
||||
db.volume_glance_metadata_delete_by_volume(ctxt, 1)
|
||||
metadata = db.volume_glance_metadata_get(ctxt, 1)
|
||||
self.assertEqual(len(metadata), 0)
|
||||
|
||||
def test_vol_glance_metadata_copy_to_snapshot(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db.volume_create(ctxt, {'id': 1})
|
||||
db.snapshot_create(ctxt, {'id': 100, 'volume_id': 1})
|
||||
vol_meta = db.volume_glance_metadata_create(ctxt, 1, 'key1',
|
||||
'value1')
|
||||
db.volume_glance_metadata_copy_to_snapshot(ctxt, 100, 1)
|
||||
|
||||
expected_meta = {'snapshot_id': '100',
|
||||
'key': 'key1',
|
||||
'value': 'value1'}
|
||||
|
||||
for meta in db.volume_snapshot_glance_metadata_get(ctxt, 100):
|
||||
for (key, value) in expected_meta.items():
|
||||
self.assertEquals(meta[key], value)
|
||||
|
||||
def test_vol_glance_metadata_copy_to_volume(self):
|
||||
ctxt = context.get_admin_context()
|
||||
db.volume_create(ctxt, {'id': 1})
|
||||
db.volume_create(ctxt, {'id': 100, 'source_volid': 1})
|
||||
vol_meta = db.volume_glance_metadata_create(ctxt, 1, 'key1',
|
||||
'value1')
|
||||
db.volume_glance_metadata_copy_from_volume_to_volume(ctxt, 100, 1)
|
||||
|
||||
expected_meta = {'id': '100',
|
||||
'key': 'key1',
|
||||
'value': 'value1'}
|
||||
|
||||
for meta in db.volume_glance_metadata_get(ctxt, 100):
|
||||
for (key, value) in expected_meta.items():
|
||||
self.assertEquals(meta[key], value)
|
|
@ -1,172 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012, Intel, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Unit Tests for manila.volume.rpcapi
|
||||
"""
|
||||
|
||||
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import flags
|
||||
from manila.openstack.common import jsonutils
|
||||
from manila.openstack.common import rpc
|
||||
from manila import test
|
||||
from manila.volume import rpcapi as volume_rpcapi
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class VolumeRpcAPITestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.context = context.get_admin_context()
|
||||
vol = {}
|
||||
vol['host'] = 'fake_host'
|
||||
vol['availability_zone'] = FLAGS.storage_availability_zone
|
||||
vol['status'] = "available"
|
||||
vol['attach_status'] = "detached"
|
||||
volume = db.volume_create(self.context, vol)
|
||||
|
||||
snpshot = {
|
||||
'volume_id': 'fake_id',
|
||||
'status': "creating",
|
||||
'progress': '0%',
|
||||
'volume_size': 0,
|
||||
'display_name': 'fake_name',
|
||||
'display_description': 'fake_description'}
|
||||
snapshot = db.snapshot_create(self.context, snpshot)
|
||||
self.fake_volume = jsonutils.to_primitive(volume)
|
||||
self.fake_snapshot = jsonutils.to_primitive(snapshot)
|
||||
super(VolumeRpcAPITestCase, self).setUp()
|
||||
|
||||
def test_serialized_volume_has_id(self):
|
||||
self.assertTrue('id' in self.fake_volume)
|
||||
|
||||
def _test_volume_api(self, method, rpc_method, **kwargs):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
if 'rpcapi_class' in kwargs:
|
||||
rpcapi_class = kwargs['rpcapi_class']
|
||||
del kwargs['rpcapi_class']
|
||||
else:
|
||||
rpcapi_class = volume_rpcapi.VolumeAPI
|
||||
rpcapi = rpcapi_class()
|
||||
expected_retval = 'foo' if method == 'call' else None
|
||||
|
||||
expected_version = kwargs.pop('version', rpcapi.BASE_RPC_API_VERSION)
|
||||
expected_msg = rpcapi.make_msg(method, **kwargs)
|
||||
if 'volume' in expected_msg['args']:
|
||||
volume = expected_msg['args']['volume']
|
||||
del expected_msg['args']['volume']
|
||||
expected_msg['args']['volume_id'] = volume['id']
|
||||
if 'snapshot' in expected_msg['args']:
|
||||
snapshot = expected_msg['args']['snapshot']
|
||||
del expected_msg['args']['snapshot']
|
||||
expected_msg['args']['snapshot_id'] = snapshot['id']
|
||||
if 'host' in expected_msg['args']:
|
||||
del expected_msg['args']['host']
|
||||
|
||||
expected_msg['version'] = expected_version
|
||||
|
||||
if 'host' in kwargs:
|
||||
host = kwargs['host']
|
||||
else:
|
||||
host = kwargs['volume']['host']
|
||||
expected_topic = '%s.%s' % (FLAGS.volume_topic, host)
|
||||
|
||||
self.fake_args = None
|
||||
self.fake_kwargs = None
|
||||
|
||||
def _fake_rpc_method(*args, **kwargs):
|
||||
self.fake_args = args
|
||||
self.fake_kwargs = kwargs
|
||||
if expected_retval:
|
||||
return expected_retval
|
||||
|
||||
self.stubs.Set(rpc, rpc_method, _fake_rpc_method)
|
||||
|
||||
retval = getattr(rpcapi, method)(ctxt, **kwargs)
|
||||
|
||||
self.assertEqual(retval, expected_retval)
|
||||
expected_args = [ctxt, expected_topic, expected_msg]
|
||||
for arg, expected_arg in zip(self.fake_args, expected_args):
|
||||
self.assertEqual(arg, expected_arg)
|
||||
|
||||
def test_create_volume(self):
|
||||
self._test_volume_api('create_volume',
|
||||
rpc_method='cast',
|
||||
volume=self.fake_volume,
|
||||
host='fake_host1',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='fake_properties',
|
||||
allow_reschedule=True,
|
||||
snapshot_id='fake_snapshot_id',
|
||||
image_id='fake_image_id',
|
||||
source_volid='fake_src_id',
|
||||
version='1.4')
|
||||
|
||||
def test_delete_volume(self):
|
||||
self._test_volume_api('delete_volume',
|
||||
rpc_method='cast',
|
||||
volume=self.fake_volume)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
self._test_volume_api('create_snapshot',
|
||||
rpc_method='cast',
|
||||
volume=self.fake_volume,
|
||||
snapshot=self.fake_snapshot)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
self._test_volume_api('delete_snapshot',
|
||||
rpc_method='cast',
|
||||
snapshot=self.fake_snapshot,
|
||||
host='fake_host')
|
||||
|
||||
def test_attach_volume(self):
|
||||
self._test_volume_api('attach_volume',
|
||||
rpc_method='call',
|
||||
volume=self.fake_volume,
|
||||
instance_uuid='fake_uuid',
|
||||
mountpoint='fake_mountpoint')
|
||||
|
||||
def test_detach_volume(self):
|
||||
self._test_volume_api('detach_volume',
|
||||
rpc_method='call',
|
||||
volume=self.fake_volume)
|
||||
|
||||
def test_copy_volume_to_image(self):
|
||||
self._test_volume_api('copy_volume_to_image',
|
||||
rpc_method='cast',
|
||||
volume=self.fake_volume,
|
||||
image_meta={'id': 'fake_image_id',
|
||||
'container_format': 'fake_type',
|
||||
'disk_format': 'fake_type'},
|
||||
version='1.3')
|
||||
|
||||
def test_initialize_connection(self):
|
||||
self._test_volume_api('initialize_connection',
|
||||
rpc_method='call',
|
||||
volume=self.fake_volume,
|
||||
connector='fake_connector')
|
||||
|
||||
def test_terminate_connection(self):
|
||||
self._test_volume_api('terminate_connection',
|
||||
rpc_method='call',
|
||||
volume=self.fake_volume,
|
||||
connector='fake_connector',
|
||||
force=False)
|
|
@ -1,187 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Zadara Storage Inc.
|
||||
# Copyright (c) 2011 OpenStack LLC.
|
||||
# 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.
|
||||
"""
|
||||
Unit Tests for volume types code
|
||||
"""
|
||||
import time
|
||||
|
||||
from manila import context
|
||||
from manila.db.sqlalchemy import models
|
||||
from manila.db.sqlalchemy import session as sql_session
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.tests import fake_flags
|
||||
from manila.volume import volume_types
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VolumeTypeTestCase(test.TestCase):
|
||||
"""Test cases for volume type code."""
|
||||
def setUp(self):
|
||||
super(VolumeTypeTestCase, self).setUp()
|
||||
|
||||
self.ctxt = context.get_admin_context()
|
||||
self.vol_type1_name = str(int(time.time()))
|
||||
self.vol_type1_specs = dict(type="physical drive",
|
||||
drive_type="SAS",
|
||||
size="300",
|
||||
rpm="7200",
|
||||
visible="True")
|
||||
|
||||
def test_volume_type_create_then_destroy(self):
|
||||
"""Ensure volume types can be created and deleted."""
|
||||
prev_all_vtypes = volume_types.get_all_types(self.ctxt)
|
||||
|
||||
type_ref = volume_types.create(self.ctxt,
|
||||
self.vol_type1_name,
|
||||
self.vol_type1_specs)
|
||||
new = volume_types.get_volume_type_by_name(self.ctxt,
|
||||
self.vol_type1_name)
|
||||
|
||||
LOG.info(_("Given data: %s"), self.vol_type1_specs)
|
||||
LOG.info(_("Result data: %s"), new)
|
||||
|
||||
for k, v in self.vol_type1_specs.iteritems():
|
||||
self.assertEqual(v, new['extra_specs'][k],
|
||||
'one of fields doesnt match')
|
||||
|
||||
new_all_vtypes = volume_types.get_all_types(self.ctxt)
|
||||
self.assertEqual(len(prev_all_vtypes) + 1,
|
||||
len(new_all_vtypes),
|
||||
'drive type was not created')
|
||||
|
||||
volume_types.destroy(self.ctxt, type_ref['id'])
|
||||
new_all_vtypes = volume_types.get_all_types(self.ctxt)
|
||||
self.assertEqual(prev_all_vtypes,
|
||||
new_all_vtypes,
|
||||
'drive type was not deleted')
|
||||
|
||||
def test_get_all_volume_types(self):
|
||||
"""Ensures that all volume types can be retrieved."""
|
||||
session = sql_session.get_session()
|
||||
total_volume_types = session.query(models.VolumeTypes).count()
|
||||
vol_types = volume_types.get_all_types(self.ctxt)
|
||||
self.assertEqual(total_volume_types, len(vol_types))
|
||||
|
||||
def test_get_default_volume_type(self):
|
||||
"""Ensures default volume type can be retrieved."""
|
||||
type_ref = volume_types.create(self.ctxt,
|
||||
fake_flags.def_vol_type,
|
||||
{})
|
||||
default_vol_type = volume_types.get_default_volume_type()
|
||||
self.assertEqual(default_vol_type.get('name'),
|
||||
fake_flags.def_vol_type)
|
||||
|
||||
def test_default_volume_type_missing_in_db(self):
|
||||
"""Ensures proper exception raised if default volume type
|
||||
is not in database."""
|
||||
session = sql_session.get_session()
|
||||
default_vol_type = volume_types.get_default_volume_type()
|
||||
self.assertEqual(default_vol_type, {})
|
||||
|
||||
def test_non_existent_vol_type_shouldnt_delete(self):
|
||||
"""Ensures that volume type creation fails with invalid args."""
|
||||
self.assertRaises(exception.VolumeTypeNotFound,
|
||||
volume_types.destroy, self.ctxt, "sfsfsdfdfs")
|
||||
|
||||
def test_repeated_vol_types_shouldnt_raise(self):
|
||||
"""Ensures that volume duplicates don't raise."""
|
||||
new_name = self.vol_type1_name + "dup"
|
||||
type_ref = volume_types.create(self.ctxt, new_name)
|
||||
volume_types.destroy(self.ctxt, type_ref['id'])
|
||||
type_ref = volume_types.create(self.ctxt, new_name)
|
||||
|
||||
def test_invalid_volume_types_params(self):
|
||||
"""Ensures that volume type creation fails with invalid args."""
|
||||
self.assertRaises(exception.InvalidVolumeType,
|
||||
volume_types.destroy, self.ctxt, None)
|
||||
self.assertRaises(exception.InvalidVolumeType,
|
||||
volume_types.get_volume_type, self.ctxt, None)
|
||||
self.assertRaises(exception.InvalidVolumeType,
|
||||
volume_types.get_volume_type_by_name,
|
||||
self.ctxt, None)
|
||||
|
||||
def test_volume_type_get_by_id_and_name(self):
|
||||
"""Ensure volume types get returns same entry."""
|
||||
volume_types.create(self.ctxt,
|
||||
self.vol_type1_name,
|
||||
self.vol_type1_specs)
|
||||
new = volume_types.get_volume_type_by_name(self.ctxt,
|
||||
self.vol_type1_name)
|
||||
|
||||
new2 = volume_types.get_volume_type(self.ctxt, new['id'])
|
||||
self.assertEqual(new, new2)
|
||||
|
||||
def test_volume_type_search_by_extra_spec(self):
|
||||
"""Ensure volume types get by extra spec returns correct type."""
|
||||
volume_types.create(self.ctxt, "type1", {"key1": "val1",
|
||||
"key2": "val2"})
|
||||
volume_types.create(self.ctxt, "type2", {"key2": "val2",
|
||||
"key3": "val3"})
|
||||
volume_types.create(self.ctxt, "type3", {"key3": "another_value",
|
||||
"key4": "val4"})
|
||||
|
||||
vol_types = volume_types.get_all_types(
|
||||
self.ctxt,
|
||||
search_opts={'extra_specs': {"key1": "val1"}})
|
||||
LOG.info("vol_types: %s" % vol_types)
|
||||
self.assertEqual(len(vol_types), 1)
|
||||
self.assertTrue("type1" in vol_types.keys())
|
||||
self.assertEqual(vol_types['type1']['extra_specs'],
|
||||
{"key1": "val1", "key2": "val2"})
|
||||
|
||||
vol_types = volume_types.get_all_types(
|
||||
self.ctxt,
|
||||
search_opts={'extra_specs': {"key2": "val2"}})
|
||||
LOG.info("vol_types: %s" % vol_types)
|
||||
self.assertEqual(len(vol_types), 2)
|
||||
self.assertTrue("type1" in vol_types.keys())
|
||||
self.assertTrue("type2" in vol_types.keys())
|
||||
|
||||
vol_types = volume_types.get_all_types(
|
||||
self.ctxt,
|
||||
search_opts={'extra_specs': {"key3": "val3"}})
|
||||
LOG.info("vol_types: %s" % vol_types)
|
||||
self.assertEqual(len(vol_types), 1)
|
||||
self.assertTrue("type2" in vol_types.keys())
|
||||
|
||||
def test_volume_type_search_by_extra_spec_multiple(self):
|
||||
"""Ensure volume types get by extra spec returns correct type."""
|
||||
volume_types.create(self.ctxt, "type1", {"key1": "val1",
|
||||
"key2": "val2",
|
||||
"key3": "val3"})
|
||||
volume_types.create(self.ctxt, "type2", {"key2": "val2",
|
||||
"key3": "val3"})
|
||||
volume_types.create(self.ctxt, "type3", {"key1": "val1",
|
||||
"key3": "val3",
|
||||
"key4": "val4"})
|
||||
|
||||
vol_types = volume_types.get_all_types(
|
||||
self.ctxt,
|
||||
search_opts={'extra_specs': {"key1": "val1",
|
||||
"key3": "val3"}})
|
||||
LOG.info("vol_types: %s" % vol_types)
|
||||
self.assertEqual(len(vol_types), 2)
|
||||
self.assertTrue("type1" in vol_types.keys())
|
||||
self.assertTrue("type3" in vol_types.keys())
|
||||
self.assertEqual(vol_types['type1']['extra_specs'],
|
||||
{"key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
self.assertEqual(vol_types['type3']['extra_specs'],
|
||||
{"key1": "val1", "key3": "val3", "key4": "val4"})
|
|
@ -1,130 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Zadara Storage Inc.
|
||||
# Copyright (c) 2011 OpenStack LLC.
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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.
|
||||
"""
|
||||
Unit Tests for volume types extra specs code
|
||||
"""
|
||||
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import test
|
||||
|
||||
|
||||
class VolumeTypeExtraSpecsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeTypeExtraSpecsTestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.vol_type1 = dict(name="TEST: Regular volume test")
|
||||
self.vol_type1_specs = dict(vol_extra1="value1",
|
||||
vol_extra2="value2",
|
||||
vol_extra3=3)
|
||||
self.vol_type1['extra_specs'] = self.vol_type1_specs
|
||||
ref = db.volume_type_create(self.context, self.vol_type1)
|
||||
self.volume_type1_id = ref.id
|
||||
for k, v in self.vol_type1_specs.iteritems():
|
||||
self.vol_type1_specs[k] = str(v)
|
||||
|
||||
self.vol_type2_noextra = dict(name="TEST: Volume type without extra")
|
||||
ref = db.volume_type_create(self.context, self.vol_type2_noextra)
|
||||
self.vol_type2_id = ref.id
|
||||
|
||||
def tearDown(self):
|
||||
# Remove the volume type from the database
|
||||
db.volume_type_destroy(context.get_admin_context(),
|
||||
self.vol_type1['id'])
|
||||
db.volume_type_destroy(context.get_admin_context(),
|
||||
self.vol_type2_noextra['id'])
|
||||
super(VolumeTypeExtraSpecsTestCase, self).tearDown()
|
||||
|
||||
def test_volume_type_specs_get(self):
|
||||
expected_specs = self.vol_type1_specs.copy()
|
||||
actual_specs = db.volume_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_volume_type_extra_specs_delete(self):
|
||||
expected_specs = self.vol_type1_specs.copy()
|
||||
del expected_specs['vol_extra2']
|
||||
db.volume_type_extra_specs_delete(context.get_admin_context(),
|
||||
self.volume_type1_id,
|
||||
'vol_extra2')
|
||||
actual_specs = db.volume_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_volume_type_extra_specs_update(self):
|
||||
expected_specs = self.vol_type1_specs.copy()
|
||||
expected_specs['vol_extra3'] = "4"
|
||||
db.volume_type_extra_specs_update_or_create(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id,
|
||||
dict(vol_extra3=4))
|
||||
actual_specs = db.volume_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_volume_type_extra_specs_create(self):
|
||||
expected_specs = self.vol_type1_specs.copy()
|
||||
expected_specs['vol_extra4'] = 'value4'
|
||||
expected_specs['vol_extra5'] = 'value5'
|
||||
db.volume_type_extra_specs_update_or_create(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id,
|
||||
dict(vol_extra4="value4",
|
||||
vol_extra5="value5"))
|
||||
actual_specs = db.volume_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_volume_type_get_with_extra_specs(self):
|
||||
volume_type = db.volume_type_get(
|
||||
context.get_admin_context(),
|
||||
self.volume_type1_id)
|
||||
self.assertEquals(volume_type['extra_specs'],
|
||||
self.vol_type1_specs)
|
||||
|
||||
volume_type = db.volume_type_get(
|
||||
context.get_admin_context(),
|
||||
self.vol_type2_id)
|
||||
self.assertEquals(volume_type['extra_specs'], {})
|
||||
|
||||
def test_volume_type_get_by_name_with_extra_specs(self):
|
||||
volume_type = db.volume_type_get_by_name(
|
||||
context.get_admin_context(),
|
||||
self.vol_type1['name'])
|
||||
self.assertEquals(volume_type['extra_specs'],
|
||||
self.vol_type1_specs)
|
||||
|
||||
volume_type = db.volume_type_get_by_name(
|
||||
context.get_admin_context(),
|
||||
self.vol_type2_noextra['name'])
|
||||
self.assertEquals(volume_type['extra_specs'], {})
|
||||
|
||||
def test_volume_type_get_all(self):
|
||||
expected_specs = self.vol_type1_specs.copy()
|
||||
|
||||
types = db.volume_type_get_all(context.get_admin_context())
|
||||
|
||||
self.assertEquals(
|
||||
types[self.vol_type1['name']]['extra_specs'], expected_specs)
|
||||
|
||||
self.assertEquals(
|
||||
types[self.vol_type2_noextra['name']]['extra_specs'], {})
|
|
@ -1,117 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 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.
|
||||
|
||||
"""Tests For miscellaneous util methods used with volume."""
|
||||
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import flags
|
||||
from manila.openstack.common import importutils
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common.notifier import api as notifier_api
|
||||
from manila.openstack.common.notifier import test_notifier
|
||||
from manila import test
|
||||
from manila.volume import utils as volume_utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class UsageInfoTestCase(test.TestCase):
|
||||
|
||||
QUEUE_NAME = 'manila-volume'
|
||||
HOSTNAME = 'my-host.com'
|
||||
HOSTIP = '10.0.0.1'
|
||||
BACKEND = 'test_backend'
|
||||
MULTI_AT_BACKEND = 'test_b@ckend'
|
||||
|
||||
def setUp(self):
|
||||
super(UsageInfoTestCase, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
host='fake',
|
||||
notification_driver=[test_notifier.__name__])
|
||||
self.volume = importutils.import_object(FLAGS.volume_manager)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.snapshot_id = 'fake'
|
||||
self.volume_size = 0
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
def tearDown(self):
|
||||
notifier_api._reset_drivers()
|
||||
super(UsageInfoTestCase, self).tearDown()
|
||||
|
||||
def _create_volume(self, params={}):
|
||||
"""Create a test volume."""
|
||||
vol = {}
|
||||
vol['snapshot_id'] = self.snapshot_id
|
||||
vol['user_id'] = self.user_id
|
||||
vol['project_id'] = self.project_id
|
||||
vol['host'] = FLAGS.host
|
||||
vol['availability_zone'] = FLAGS.storage_availability_zone
|
||||
vol['status'] = "creating"
|
||||
vol['attach_status'] = "detached"
|
||||
vol['size'] = self.volume_size
|
||||
vol.update(params)
|
||||
return db.volume_create(self.context, vol)['id']
|
||||
|
||||
def test_notify_usage_exists(self):
|
||||
"""Ensure 'exists' notification generates appropriate usage data."""
|
||||
volume_id = self._create_volume()
|
||||
volume = db.volume_get(self.context, volume_id)
|
||||
volume_utils.notify_usage_exists(self.context, volume)
|
||||
LOG.info("%r" % test_notifier.NOTIFICATIONS)
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
msg = test_notifier.NOTIFICATIONS[0]
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'volume.exists')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project_id)
|
||||
self.assertEquals(payload['user_id'], self.user_id)
|
||||
self.assertEquals(payload['snapshot_id'], self.snapshot_id)
|
||||
self.assertEquals(payload['volume_id'], volume.id)
|
||||
self.assertEquals(payload['size'], self.volume_size)
|
||||
for attr in ('display_name', 'created_at', 'launched_at',
|
||||
'status', 'audit_period_beginning',
|
||||
'audit_period_ending'):
|
||||
self.assertTrue(attr in payload,
|
||||
msg="Key %s not in payload" % attr)
|
||||
db.volume_destroy(context.get_admin_context(), volume['id'])
|
||||
|
||||
def test_get_host_from_queue_simple(self):
|
||||
fullname = "%s.%s@%s" % (self.QUEUE_NAME, self.HOSTNAME, self.BACKEND)
|
||||
self.assertEquals(volume_utils.get_host_from_queue(fullname),
|
||||
self.HOSTNAME)
|
||||
|
||||
def test_get_host_from_queue_ip(self):
|
||||
fullname = "%s.%s@%s" % (self.QUEUE_NAME, self.HOSTIP, self.BACKEND)
|
||||
self.assertEquals(volume_utils.get_host_from_queue(fullname),
|
||||
self.HOSTIP)
|
||||
|
||||
def test_get_host_from_queue_multi_at_symbol(self):
|
||||
fullname = "%s.%s@%s" % (self.QUEUE_NAME, self.HOSTNAME,
|
||||
self.MULTI_AT_BACKEND)
|
||||
self.assertEquals(volume_utils.get_host_from_queue(fullname),
|
||||
self.HOSTNAME)
|
||||
|
||||
def test_get_host_from_queue_ip_multi_at_symbol(self):
|
||||
fullname = "%s.%s@%s" % (self.QUEUE_NAME, self.HOSTIP,
|
||||
self.MULTI_AT_BACKEND)
|
||||
self.assertEquals(volume_utils.get_host_from_queue(fullname),
|
||||
self.HOSTIP)
|
|
@ -1,220 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Pedro Navarro Perez
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Unit tests for Windows Server 2012 OpenStack Manila volume driver
|
||||
"""
|
||||
import sys
|
||||
|
||||
import manila.flags
|
||||
from manila.tests.windows import basetestcase
|
||||
from manila.tests.windows import db_fakes
|
||||
from manila.tests.windows import windowsutils
|
||||
from manila.volume.drivers import windows
|
||||
|
||||
FLAGS = manila.flags.FLAGS
|
||||
|
||||
|
||||
class TestWindowsDriver(basetestcase.BaseTestCase):
|
||||
|
||||
def __init__(self, method):
|
||||
super(TestWindowsDriver, self).__init__(method)
|
||||
|
||||
def setUp(self):
|
||||
super(TestWindowsDriver, self).setUp()
|
||||
self.flags(
|
||||
windows_iscsi_lun_path='C:\iSCSIVirtualDisks',
|
||||
)
|
||||
self._volume_data = None
|
||||
self._volume_data_2 = None
|
||||
self._snapshot_data = None
|
||||
self._connector_data = None
|
||||
self._volume_id = '10958016-e196-42e3-9e7f-5d8927ae3099'
|
||||
self._volume_id_2 = '20958016-e196-42e3-9e7f-5d8927ae3098'
|
||||
self._snapshot_id = '30958016-e196-42e3-9e7f-5d8927ae3097'
|
||||
self._iqn = "iqn.1991-05.com.microsoft:dell1160dsy"
|
||||
|
||||
self._setup_stubs()
|
||||
|
||||
self._drv = windows.WindowsDriver()
|
||||
self._drv.do_setup({})
|
||||
self._wutils = windowsutils.WindowsUtils()
|
||||
|
||||
def _setup_stubs(self):
|
||||
|
||||
# Modules to mock
|
||||
modules_to_mock = [
|
||||
'wmi',
|
||||
'os',
|
||||
'subprocess',
|
||||
'multiprocessing'
|
||||
]
|
||||
|
||||
modules_to_test = [
|
||||
windows,
|
||||
windowsutils,
|
||||
sys.modules[__name__]
|
||||
]
|
||||
|
||||
self._inject_mocks_in_modules(modules_to_mock, modules_to_test)
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
if (self._volume_data_2 and
|
||||
self._wutils.volume_exists(self._volume_data_2['name'])):
|
||||
self._wutils.delete_volume(self._volume_data_2['name'])
|
||||
|
||||
if (self._volume_data and
|
||||
self._wutils.volume_exists(
|
||||
self._volume_data['name'])):
|
||||
self._wutils.delete_volume(self._volume_data['name'])
|
||||
if (self._snapshot_data and
|
||||
self._wutils.snapshot_exists(
|
||||
self._snapshot_data['name'])):
|
||||
self._wutils.delete_snapshot(self._snapshot_data['name'])
|
||||
if (self._connector_data and
|
||||
self._wutils.initiator_id_exists(
|
||||
"%s%s" % (FLAGS.iscsi_target_prefix,
|
||||
self._volume_data['name']),
|
||||
self._connector_data['initiator'])):
|
||||
target_name = "%s%s" % (FLAGS.iscsi_target_prefix,
|
||||
self._volume_data['name'])
|
||||
initiator_name = self._connector_data['initiator']
|
||||
self._wutils.delete_initiator_id(target_name, initiator_name)
|
||||
if (self._volume_data and
|
||||
self._wutils.export_exists("%s%s" %
|
||||
(FLAGS.iscsi_target_prefix,
|
||||
self._volume_data['name']))):
|
||||
self._wutils.delete_export(
|
||||
"%s%s" % (FLAGS.iscsi_target_prefix,
|
||||
self._volume_data['name']))
|
||||
|
||||
finally:
|
||||
super(TestWindowsDriver, self).tearDown()
|
||||
|
||||
def test_check_for_setup_errors(self):
|
||||
self._drv.check_for_setup_error()
|
||||
|
||||
def test_create_volume(self):
|
||||
self._create_volume()
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name'])
|
||||
self.assertEquals(len(wt_disks), 1)
|
||||
|
||||
def _create_volume(self):
|
||||
self._volume_data = db_fakes.get_fake_volume_info(self._volume_id)
|
||||
self._drv.create_volume(self._volume_data)
|
||||
|
||||
def test_delete_volume(self):
|
||||
self._create_volume()
|
||||
|
||||
self._drv.delete_volume(self._volume_data)
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name'])
|
||||
self.assertEquals(len(wt_disks), 0)
|
||||
|
||||
def test_create_snapshot(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data['name'])
|
||||
self.assertEquals(len(wt_disks), 1)
|
||||
#Create a snapshot from the previous volume
|
||||
self._create_snapshot()
|
||||
|
||||
snapshot_name = self._snapshot_data['name']
|
||||
wt_snapshots = self._wutils.find_snapshot_by_name(snapshot_name)
|
||||
self.assertEquals(len(wt_snapshots), 1)
|
||||
|
||||
def _create_snapshot(self):
|
||||
volume_name = self._volume_data['name']
|
||||
snapshot_id = self._snapshot_id
|
||||
self._snapshot_data = db_fakes.get_fake_snapshot_info(volume_name,
|
||||
snapshot_id)
|
||||
self._drv.create_snapshot(self._snapshot_data)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
#Create a snapshot from the previous volume
|
||||
self._create_snapshot()
|
||||
|
||||
self._volume_data_2 = db_fakes.get_fake_volume_info(self._volume_id_2)
|
||||
|
||||
self._drv.create_volume_from_snapshot(self._volume_data_2,
|
||||
self._snapshot_data)
|
||||
|
||||
wt_disks = self._wutils.find_vhd_by_name(self._volume_data_2['name'])
|
||||
self.assertEquals(len(wt_disks), 1)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
#Create a snapshot from the previous volume
|
||||
self._create_snapshot()
|
||||
|
||||
self._drv.delete_snapshot(self._snapshot_data)
|
||||
|
||||
snapshot_name = self._snapshot_data['name']
|
||||
wt_snapshots = self._wutils.find_snapshot_by_name(snapshot_name)
|
||||
self.assertEquals(len(wt_snapshots), 0)
|
||||
|
||||
def test_create_export(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
|
||||
retval = self._drv.create_export({}, self._volume_data)
|
||||
|
||||
volume_name = self._volume_data['name']
|
||||
self.assertEquals(
|
||||
retval,
|
||||
{'provider_location': "%s%s" % (FLAGS.iscsi_target_prefix,
|
||||
volume_name)})
|
||||
|
||||
def test_initialize_connection(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
|
||||
self._drv.create_export({}, self._volume_data)
|
||||
|
||||
self._connector_data = db_fakes.get_fake_connector_info(self._iqn)
|
||||
|
||||
init_data = self._drv.initialize_connection(self._volume_data,
|
||||
self._connector_data)
|
||||
target_name = self._volume_data['provider_location']
|
||||
initiator_name = self._connector_data['initiator']
|
||||
|
||||
wt_initiator_ids = self._wutils.find_initiator_ids(target_name,
|
||||
initiator_name)
|
||||
self.assertEquals(len(wt_initiator_ids), 1)
|
||||
|
||||
properties = init_data['data']
|
||||
self.assertNotEqual(properties['target_iqn'], None)
|
||||
|
||||
def test_ensure_export(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
|
||||
self._drv.ensure_export({}, self._volume_data)
|
||||
|
||||
def test_remove_export(self):
|
||||
#Create a volume
|
||||
self._create_volume()
|
||||
|
||||
self._drv.create_export({}, self._volume_data)
|
||||
|
||||
self._drv.remove_export({}, self._volume_data)
|
|
@ -1,509 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
import contextlib
|
||||
import StringIO
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import mox
|
||||
from oslo.config import cfg
|
||||
|
||||
from manila.db import api as db_api
|
||||
from manila import exception
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume import driver as parent_driver
|
||||
from manila.volume.drivers.xenapi import lib
|
||||
from manila.volume.drivers.xenapi import sm as driver
|
||||
from manila.volume.drivers.xenapi import tools
|
||||
|
||||
|
||||
class MockContext(object):
|
||||
def __init__(ctxt, auth_token):
|
||||
ctxt.auth_token = auth_token
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def simple_context(value):
|
||||
yield value
|
||||
|
||||
|
||||
def get_configured_driver(server='ignore_server', path='ignore_path'):
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.xenapi_nfs_server = server
|
||||
configuration.xenapi_nfs_serverpath = path
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
return driver.XenAPINFSDriver(configuration=configuration)
|
||||
|
||||
|
||||
class DriverTestCase(unittest.TestCase):
|
||||
|
||||
def assert_flag(self, flagname):
|
||||
self.assertTrue(hasattr(driver.FLAGS, flagname))
|
||||
|
||||
def test_config_options(self):
|
||||
self.assert_flag('xenapi_connection_url')
|
||||
self.assert_flag('xenapi_connection_username')
|
||||
self.assert_flag('xenapi_connection_password')
|
||||
self.assert_flag('xenapi_nfs_server')
|
||||
self.assert_flag('xenapi_nfs_serverpath')
|
||||
self.assert_flag('xenapi_sr_base_path')
|
||||
|
||||
def test_do_setup(self):
|
||||
mock = mox.Mox()
|
||||
mock.StubOutWithMock(driver, 'xenapi_lib')
|
||||
mock.StubOutWithMock(driver, 'xenapi_opts')
|
||||
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.xenapi_connection_url = 'url'
|
||||
configuration.xenapi_connection_username = 'user'
|
||||
configuration.xenapi_connection_password = 'pass'
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
session_factory = object()
|
||||
nfsops = object()
|
||||
|
||||
driver.xenapi_lib.SessionFactory('url', 'user', 'pass').AndReturn(
|
||||
session_factory)
|
||||
|
||||
driver.xenapi_lib.NFSBasedVolumeOperations(
|
||||
session_factory).AndReturn(nfsops)
|
||||
|
||||
drv = driver.XenAPINFSDriver(configuration=configuration)
|
||||
|
||||
mock.ReplayAll()
|
||||
drv.do_setup('context')
|
||||
mock.VerifyAll()
|
||||
|
||||
self.assertEquals(nfsops, drv.nfs_ops)
|
||||
|
||||
def test_create_volume(self):
|
||||
mock = mox.Mox()
|
||||
|
||||
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
|
||||
drv = get_configured_driver('server', 'path')
|
||||
drv.nfs_ops = ops
|
||||
|
||||
volume_details = dict(
|
||||
sr_uuid='sr_uuid',
|
||||
vdi_uuid='vdi_uuid'
|
||||
)
|
||||
ops.create_volume(
|
||||
'server', 'path', 1, 'name', 'desc').AndReturn(volume_details)
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.create_volume(dict(
|
||||
size=1, display_name='name', display_description='desc'))
|
||||
mock.VerifyAll()
|
||||
|
||||
self.assertEquals(dict(provider_location='sr_uuid/vdi_uuid'), result)
|
||||
|
||||
def test_delete_volume(self):
|
||||
mock = mox.Mox()
|
||||
|
||||
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
|
||||
drv = get_configured_driver('server', 'path')
|
||||
drv.nfs_ops = ops
|
||||
|
||||
ops.delete_volume('server', 'path', 'sr_uuid', 'vdi_uuid')
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.delete_volume(dict(
|
||||
provider_location='sr_uuid/vdi_uuid'))
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_create_export_does_not_raise_exception(self):
|
||||
configuration = conf.Configuration([])
|
||||
drv = driver.XenAPINFSDriver(configuration=configuration)
|
||||
drv.create_export('context', 'volume')
|
||||
|
||||
def test_remove_export_does_not_raise_exception(self):
|
||||
configuration = conf.Configuration([])
|
||||
drv = driver.XenAPINFSDriver(configuration=configuration)
|
||||
drv.remove_export('context', 'volume')
|
||||
|
||||
def test_initialize_connection(self):
|
||||
mock = mox.Mox()
|
||||
|
||||
drv = get_configured_driver('server', 'path')
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.initialize_connection(
|
||||
dict(
|
||||
display_name='name',
|
||||
display_description='desc',
|
||||
provider_location='sr_uuid/vdi_uuid'),
|
||||
'connector'
|
||||
)
|
||||
mock.VerifyAll()
|
||||
|
||||
self.assertEquals(
|
||||
dict(
|
||||
driver_volume_type='xensm',
|
||||
data=dict(
|
||||
name_label='name',
|
||||
name_description='desc',
|
||||
sr_uuid='sr_uuid',
|
||||
vdi_uuid='vdi_uuid',
|
||||
sr_type='nfs',
|
||||
server='server',
|
||||
serverpath='path',
|
||||
introduce_sr_keys=['sr_type', 'server', 'serverpath']
|
||||
)
|
||||
),
|
||||
result
|
||||
)
|
||||
|
||||
def test_initialize_connection_null_values(self):
|
||||
mock = mox.Mox()
|
||||
|
||||
drv = get_configured_driver('server', 'path')
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.initialize_connection(
|
||||
dict(
|
||||
display_name=None,
|
||||
display_description=None,
|
||||
provider_location='sr_uuid/vdi_uuid'),
|
||||
'connector'
|
||||
)
|
||||
mock.VerifyAll()
|
||||
|
||||
self.assertEquals(
|
||||
dict(
|
||||
driver_volume_type='xensm',
|
||||
data=dict(
|
||||
name_label='',
|
||||
name_description='',
|
||||
sr_uuid='sr_uuid',
|
||||
vdi_uuid='vdi_uuid',
|
||||
sr_type='nfs',
|
||||
server='server',
|
||||
serverpath='path',
|
||||
introduce_sr_keys=['sr_type', 'server', 'serverpath']
|
||||
)
|
||||
),
|
||||
result
|
||||
)
|
||||
|
||||
def _setup_mock_driver(self, server, serverpath, sr_base_path="_srbp"):
|
||||
mock = mox.Mox()
|
||||
|
||||
drv = get_configured_driver(server, serverpath)
|
||||
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
|
||||
db = mock.CreateMock(db_api)
|
||||
drv.nfs_ops = ops
|
||||
drv.db = db
|
||||
|
||||
mock.StubOutWithMock(driver, 'FLAGS')
|
||||
driver.FLAGS.xenapi_nfs_server = server
|
||||
driver.FLAGS.xenapi_nfs_serverpath = serverpath
|
||||
driver.FLAGS.xenapi_sr_base_path = sr_base_path
|
||||
|
||||
return mock, drv
|
||||
|
||||
def test_create_snapshot(self):
|
||||
mock, drv = self._setup_mock_driver('server', 'serverpath')
|
||||
|
||||
snapshot = dict(
|
||||
volume_id="volume-id",
|
||||
display_name="snapshot-name",
|
||||
display_description="snapshot-desc",
|
||||
volume=dict(provider_location="sr-uuid/vdi-uuid"))
|
||||
|
||||
drv.nfs_ops.copy_volume(
|
||||
"server", "serverpath", "sr-uuid", "vdi-uuid",
|
||||
"snapshot-name", "snapshot-desc"
|
||||
).AndReturn(dict(sr_uuid="copied-sr", vdi_uuid="copied-vdi"))
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.create_snapshot(snapshot)
|
||||
mock.VerifyAll()
|
||||
self.assertEquals(
|
||||
dict(provider_location="copied-sr/copied-vdi"),
|
||||
result)
|
||||
|
||||
def test_create_volume_from_snapshot(self):
|
||||
mock, drv = self._setup_mock_driver('server', 'serverpath')
|
||||
|
||||
snapshot = dict(
|
||||
provider_location='src-sr-uuid/src-vdi-uuid')
|
||||
volume = dict(
|
||||
display_name='tgt-name', name_description='tgt-desc')
|
||||
|
||||
drv.nfs_ops.copy_volume(
|
||||
"server", "serverpath", "src-sr-uuid", "src-vdi-uuid",
|
||||
"tgt-name", "tgt-desc"
|
||||
).AndReturn(dict(sr_uuid="copied-sr", vdi_uuid="copied-vdi"))
|
||||
|
||||
mock.ReplayAll()
|
||||
result = drv.create_volume_from_snapshot(volume, snapshot)
|
||||
mock.VerifyAll()
|
||||
|
||||
self.assertEquals(
|
||||
dict(provider_location='copied-sr/copied-vdi'), result)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
mock, drv = self._setup_mock_driver('server', 'serverpath')
|
||||
|
||||
snapshot = dict(
|
||||
provider_location='src-sr-uuid/src-vdi-uuid')
|
||||
|
||||
drv.nfs_ops.delete_volume(
|
||||
"server", "serverpath", "src-sr-uuid", "src-vdi-uuid")
|
||||
|
||||
mock.ReplayAll()
|
||||
drv.delete_snapshot(snapshot)
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_copy_volume_to_image_xenserver_case(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
mock.StubOutWithMock(drv, '_use_glance_plugin_to_upload_volume')
|
||||
mock.StubOutWithMock(driver, 'is_xenserver_format')
|
||||
context = MockContext('token')
|
||||
|
||||
driver.is_xenserver_format('image_meta').AndReturn(True)
|
||||
|
||||
drv._use_glance_plugin_to_upload_volume(
|
||||
context, 'volume', 'image_service', 'image_meta').AndReturn(
|
||||
'result')
|
||||
mock.ReplayAll()
|
||||
|
||||
result = drv.copy_volume_to_image(
|
||||
context, "volume", "image_service", "image_meta")
|
||||
self.assertEquals('result', result)
|
||||
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_copy_volume_to_image_non_xenserver_case(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
mock.StubOutWithMock(drv, '_use_image_utils_to_upload_volume')
|
||||
mock.StubOutWithMock(driver, 'is_xenserver_format')
|
||||
context = MockContext('token')
|
||||
|
||||
driver.is_xenserver_format('image_meta').AndReturn(False)
|
||||
|
||||
drv._use_image_utils_to_upload_volume(
|
||||
context, 'volume', 'image_service', 'image_meta').AndReturn(
|
||||
'result')
|
||||
mock.ReplayAll()
|
||||
|
||||
result = drv.copy_volume_to_image(
|
||||
context, "volume", "image_service", "image_meta")
|
||||
self.assertEquals('result', result)
|
||||
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_use_image_utils_to_upload_volume(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
volume = dict(provider_location='sr-uuid/vdi-uuid')
|
||||
context = MockContext('token')
|
||||
|
||||
mock.StubOutWithMock(driver.image_utils, 'upload_volume')
|
||||
|
||||
drv.nfs_ops.volume_attached_here(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', True).AndReturn(
|
||||
simple_context('device'))
|
||||
|
||||
driver.image_utils.upload_volume(
|
||||
context, 'image_service', 'image_meta', 'device')
|
||||
|
||||
mock.ReplayAll()
|
||||
drv._use_image_utils_to_upload_volume(
|
||||
context, volume, "image_service", "image_meta")
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_use_glance_plugin_to_upload_volume(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
volume = dict(provider_location='sr-uuid/vdi-uuid')
|
||||
context = MockContext('token')
|
||||
|
||||
mock.StubOutWithMock(driver.glance, 'get_api_servers')
|
||||
|
||||
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
|
||||
|
||||
drv.nfs_ops.use_glance_plugin_to_upload_volume(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
|
||||
'image-id', 'token', '/var/run/sr-mount')
|
||||
|
||||
mock.ReplayAll()
|
||||
drv._use_glance_plugin_to_upload_volume(
|
||||
context, volume, "image_service", {"id": "image-id"})
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_copy_image_to_volume_xenserver_case(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
mock.StubOutWithMock(drv, '_use_glance_plugin_to_copy_image_to_volume')
|
||||
mock.StubOutWithMock(driver, 'is_xenserver_image')
|
||||
context = MockContext('token')
|
||||
|
||||
driver.is_xenserver_image(
|
||||
context, 'image_service', 'image_id').AndReturn(True)
|
||||
drv._use_glance_plugin_to_copy_image_to_volume(
|
||||
context, 'volume', 'image_service', 'image_id').AndReturn('result')
|
||||
mock.ReplayAll()
|
||||
result = drv.copy_image_to_volume(
|
||||
context, "volume", "image_service", "image_id")
|
||||
self.assertEquals('result', result)
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_copy_image_to_volume_non_xenserver_case(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
mock.StubOutWithMock(drv, '_use_image_utils_to_pipe_bytes_to_volume')
|
||||
mock.StubOutWithMock(driver, 'is_xenserver_image')
|
||||
context = MockContext('token')
|
||||
|
||||
driver.is_xenserver_image(
|
||||
context, 'image_service', 'image_id').AndReturn(False)
|
||||
drv._use_image_utils_to_pipe_bytes_to_volume(
|
||||
context, 'volume', 'image_service', 'image_id').AndReturn(True)
|
||||
mock.ReplayAll()
|
||||
drv.copy_image_to_volume(
|
||||
context, "volume", "image_service", "image_id")
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_use_image_utils_to_pipe_bytes_to_volume(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
volume = dict(provider_location='sr-uuid/vdi-uuid')
|
||||
context = MockContext('token')
|
||||
|
||||
mock.StubOutWithMock(driver.image_utils, 'fetch_to_raw')
|
||||
|
||||
drv.nfs_ops.volume_attached_here(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', False).AndReturn(
|
||||
simple_context('device'))
|
||||
|
||||
driver.image_utils.fetch_to_raw(
|
||||
context, 'image_service', 'image_id', 'device')
|
||||
|
||||
mock.ReplayAll()
|
||||
drv._use_image_utils_to_pipe_bytes_to_volume(
|
||||
context, volume, "image_service", "image_id")
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_use_glance_plugin_to_copy_image_to_volume_success(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
volume = dict(
|
||||
provider_location='sr-uuid/vdi-uuid',
|
||||
size=2)
|
||||
|
||||
mock.StubOutWithMock(driver.glance, 'get_api_servers')
|
||||
|
||||
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
|
||||
|
||||
drv.nfs_ops.use_glance_plugin_to_overwrite_volume(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
|
||||
'image_id', 'token', '/var/run/sr-mount').AndReturn(True)
|
||||
|
||||
drv.nfs_ops.resize_volume(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 2)
|
||||
|
||||
mock.ReplayAll()
|
||||
drv._use_glance_plugin_to_copy_image_to_volume(
|
||||
MockContext('token'), volume, "ignore", "image_id")
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_use_glance_plugin_to_copy_image_to_volume_fail(self):
|
||||
mock, drv = self._setup_mock_driver(
|
||||
'server', 'serverpath', '/var/run/sr-mount')
|
||||
|
||||
volume = dict(
|
||||
provider_location='sr-uuid/vdi-uuid',
|
||||
size=2)
|
||||
|
||||
mock.StubOutWithMock(driver.glance, 'get_api_servers')
|
||||
|
||||
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
|
||||
|
||||
drv.nfs_ops.use_glance_plugin_to_overwrite_volume(
|
||||
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
|
||||
'image_id', 'token', '/var/run/sr-mount').AndReturn(False)
|
||||
|
||||
mock.ReplayAll()
|
||||
|
||||
self.assertRaises(
|
||||
exception.ImageCopyFailure,
|
||||
lambda: drv._use_glance_plugin_to_copy_image_to_volume(
|
||||
MockContext('token'), volume, "ignore", "image_id"))
|
||||
|
||||
mock.VerifyAll()
|
||||
|
||||
def test_get_volume_stats_reports_required_keys(self):
|
||||
drv = get_configured_driver()
|
||||
|
||||
stats = drv.get_volume_stats()
|
||||
|
||||
required_metrics = [
|
||||
'volume_backend_name', 'vendor_name', 'driver_version',
|
||||
'storage_protocol', 'total_capacity_gb', 'free_capacity_gb',
|
||||
'reserved_percentage'
|
||||
]
|
||||
|
||||
for metric in required_metrics:
|
||||
self.assertTrue(metric in stats)
|
||||
|
||||
def test_get_volume_stats_reports_unknown_cap(self):
|
||||
drv = get_configured_driver()
|
||||
|
||||
stats = drv.get_volume_stats()
|
||||
|
||||
self.assertEquals('unknown', stats['free_capacity_gb'])
|
||||
|
||||
def test_reported_driver_type(self):
|
||||
drv = get_configured_driver()
|
||||
|
||||
stats = drv.get_volume_stats()
|
||||
|
||||
self.assertEquals('xensm', stats['storage_protocol'])
|
||||
|
||||
|
||||
class ToolsTest(unittest.TestCase):
|
||||
@mock.patch('manila.volume.drivers.xenapi.tools._stripped_first_line_of')
|
||||
def test_get_this_vm_uuid(self, mock_read_first_line):
|
||||
mock_read_first_line.return_value = 'someuuid'
|
||||
self.assertEquals('someuuid', tools.get_this_vm_uuid())
|
||||
mock_read_first_line.assert_called_once_with('/sys/hypervisor/uuid')
|
||||
|
||||
def test_stripped_first_line_of(self):
|
||||
mock_context_manager = mock.Mock()
|
||||
mock_context_manager.__enter__ = mock.Mock(
|
||||
return_value=StringIO.StringIO(' blah \n second line \n'))
|
||||
mock_context_manager.__exit__ = mock.Mock(return_value=False)
|
||||
mock_open = mock.Mock(return_value=mock_context_manager)
|
||||
|
||||
with mock.patch('__builtin__.open', mock_open):
|
||||
self.assertEquals(
|
||||
'blah', tools._stripped_first_line_of('/somefile'))
|
||||
|
||||
mock_open.assert_called_once_with('/somefile', 'rb')
|
|
@ -1,245 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 IBM Corp.
|
||||
# Copyright (c) 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.
|
||||
#
|
||||
# Authors:
|
||||
# Erik Zaadi <erikz@il.ibm.com>
|
||||
# Avishay Traeger <avishay@il.ibm.com>
|
||||
|
||||
import mox
|
||||
|
||||
from manila import exception
|
||||
from manila import flags
|
||||
from manila import test
|
||||
from manila.volume import configuration as conf
|
||||
from manila.volume.drivers import xiv
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
FAKE = "fake"
|
||||
VOLUME = {'size': 16,
|
||||
'name': FAKE,
|
||||
'id': 1}
|
||||
|
||||
CONNECTOR = {'initiator': "iqn.2012-07.org.fake:01:948f189c4695", }
|
||||
|
||||
|
||||
class XIVFakeProxyDriver(object):
|
||||
"""Fake XIV Proxy Driver."""
|
||||
|
||||
def __init__(self, xiv_info, logger, expt):
|
||||
"""
|
||||
Initialize Proxy
|
||||
"""
|
||||
|
||||
self.xiv_info = xiv_info
|
||||
self.logger = logger
|
||||
self.exception = expt
|
||||
self.xiv_portal = \
|
||||
self.xiv_iqn = FAKE
|
||||
|
||||
self.volumes = {}
|
||||
|
||||
def setup(self, context):
|
||||
if self.xiv_info['xiv_user'] != FLAGS.san_login:
|
||||
raise self.exception.NotAuthorized()
|
||||
|
||||
if self.xiv_info['xiv_address'] != FLAGS.san_ip:
|
||||
raise self.exception.HostNotFound(host='fake')
|
||||
|
||||
def create_volume(self, volume):
|
||||
if volume['size'] > 100:
|
||||
raise self.exception.VolumeBackendAPIException(data='blah')
|
||||
self.volumes[volume['name']] = volume
|
||||
|
||||
def volume_exists(self, volume):
|
||||
return self.volumes.get(volume['name'], None) is not None
|
||||
|
||||
def delete_volume(self, volume):
|
||||
if self.volumes.get(volume['name'], None) is not None:
|
||||
del self.volumes[volume['name']]
|
||||
|
||||
def initialize_connection(self, volume, connector):
|
||||
if not self.volume_exists(volume):
|
||||
raise self.exception.VolumeNotFound(volume_id=volume['id'])
|
||||
lun_id = volume['id']
|
||||
|
||||
self.volumes[volume['name']]['attached'] = connector
|
||||
|
||||
return {'driver_volume_type': 'iscsi',
|
||||
'data': {'target_discovered': True,
|
||||
'target_discovered': True,
|
||||
'target_portal': self.xiv_portal,
|
||||
'target_iqn': self.xiv_iqn,
|
||||
'target_lun': lun_id,
|
||||
'volume_id': volume['id'],
|
||||
'multipath': True,
|
||||
'provider_location': "%s,1 %s %s" % (
|
||||
self.xiv_portal,
|
||||
self.xiv_iqn,
|
||||
lun_id), },
|
||||
}
|
||||
|
||||
def terminate_connection(self, volume, connector):
|
||||
if not self.volume_exists(volume):
|
||||
raise self.exception.VolumeNotFound(volume_id=volume['id'])
|
||||
if not self.is_volume_attached(volume, connector):
|
||||
raise self.exception.VolumeNotFoundForInstance(instance_id='fake')
|
||||
del self.volumes[volume['name']]['attached']
|
||||
|
||||
def is_volume_attached(self, volume, connector):
|
||||
if not self.volume_exists(volume):
|
||||
raise self.exception.VolumeNotFound(volume_id=volume['id'])
|
||||
|
||||
return (self.volumes[volume['name']].get('attached', None)
|
||||
== connector)
|
||||
|
||||
|
||||
class XIVVolumeDriverTest(test.TestCase):
|
||||
"""Test IBM XIV volume driver."""
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize IVM XIV Driver."""
|
||||
super(XIVVolumeDriverTest, self).setUp()
|
||||
|
||||
configuration = mox.MockObject(conf.Configuration)
|
||||
configuration.san_is_local = False
|
||||
configuration.append_config_values(mox.IgnoreArg())
|
||||
|
||||
self.driver = xiv.XIVDriver(configuration=configuration)
|
||||
|
||||
def test_initialized_should_set_xiv_info(self):
|
||||
"""Test that the san flags are passed to the XIV proxy."""
|
||||
|
||||
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_user'],
|
||||
FLAGS.san_login)
|
||||
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_pass'],
|
||||
FLAGS.san_password)
|
||||
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_address'],
|
||||
FLAGS.san_ip)
|
||||
self.assertEquals(self.driver.xiv_proxy.xiv_info['xiv_vol_pool'],
|
||||
FLAGS.san_clustername)
|
||||
|
||||
def test_setup_should_fail_if_credentials_are_invalid(self):
|
||||
"""Test that the xiv_proxy validates credentials."""
|
||||
|
||||
self.driver.xiv_proxy.xiv_info['xiv_user'] = 'invalid'
|
||||
self.assertRaises(exception.NotAuthorized, self.driver.do_setup, None)
|
||||
|
||||
def test_setup_should_fail_if_connection_is_invalid(self):
|
||||
"""Test that the xiv_proxy validates connection."""
|
||||
|
||||
self.driver.xiv_proxy.xiv_info['xiv_address'] = 'invalid'
|
||||
self.assertRaises(exception.HostNotFound, self.driver.do_setup, None)
|
||||
|
||||
def test_create_volume(self):
|
||||
"""Test creating a volume."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.create_volume(VOLUME)
|
||||
has_volume = self.driver.xiv_proxy.volume_exists(VOLUME)
|
||||
self.assertTrue(has_volume)
|
||||
self.driver.delete_volume(VOLUME)
|
||||
|
||||
def test_volume_exists(self):
|
||||
"""Test the volume exist method with a volume that doesn't exist."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.assertFalse(self.driver.xiv_proxy.volume_exists({'name': FAKE}))
|
||||
|
||||
def test_delete_volume(self):
|
||||
"""Verify that a volume is deleted."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.create_volume(VOLUME)
|
||||
self.driver.delete_volume(VOLUME)
|
||||
has_volume = self.driver.xiv_proxy.volume_exists(VOLUME)
|
||||
self.assertFalse(has_volume)
|
||||
|
||||
def test_delete_volume_should_fail_for_not_existing_volume(self):
|
||||
"""Verify that deleting a non-existing volume is OK."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.delete_volume(VOLUME)
|
||||
|
||||
def test_create_volume_should_fail_if_no_pool_space_left(self):
|
||||
"""Vertify that the xiv_proxy validates volume pool space."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.assertRaises(exception.ShareBackendAPIException,
|
||||
self.driver.create_volume,
|
||||
{'name': FAKE,
|
||||
'id': 1,
|
||||
'size': 12000})
|
||||
|
||||
def test_initialize_connection(self):
|
||||
"""Test that inititialize connection attaches volume to host."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.create_volume(VOLUME)
|
||||
self.driver.initialize_connection(VOLUME, CONNECTOR)
|
||||
|
||||
self.assertTrue(
|
||||
self.driver.xiv_proxy.is_volume_attached(VOLUME, CONNECTOR))
|
||||
|
||||
self.driver.terminate_connection(VOLUME, CONNECTOR)
|
||||
self.driver.delete_volume(VOLUME)
|
||||
|
||||
def test_initialize_connection_should_fail_for_non_existing_volume(self):
|
||||
"""Verify that initialize won't work for non-existing volume."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.initialize_connection,
|
||||
VOLUME,
|
||||
CONNECTOR)
|
||||
|
||||
def test_terminate_connection(self):
|
||||
"""Test terminating a connection."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.create_volume(VOLUME)
|
||||
self.driver.initialize_connection(VOLUME, CONNECTOR)
|
||||
self.driver.terminate_connection(VOLUME, CONNECTOR)
|
||||
|
||||
self.assertFalse(self.driver.xiv_proxy.is_volume_attached(VOLUME,
|
||||
CONNECTOR))
|
||||
|
||||
self.driver.delete_volume(VOLUME)
|
||||
|
||||
def test_terminate_connection_should_fail_on_non_existing_volume(self):
|
||||
"""Test that terminate won't work for non-existing volumes."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.terminate_connection,
|
||||
VOLUME,
|
||||
CONNECTOR)
|
||||
|
||||
def test_terminate_connection_should_fail_on_non_attached_volume(self):
|
||||
"""Test that terminate won't work for volumes that are not attached."""
|
||||
|
||||
self.driver.do_setup(None)
|
||||
self.driver.create_volume(VOLUME)
|
||||
|
||||
self.assertRaises(exception.VolumeNotFoundForInstance,
|
||||
self.driver.terminate_connection,
|
||||
VOLUME,
|
||||
CONNECTOR)
|
||||
|
||||
self.driver.delete_volume(VOLUME)
|
|
@ -1,581 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2012 Zadara Storage, Inc.
|
||||
# Copyright (c) 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.
|
||||
"""
|
||||
Tests for Zadara VPSA volume driver
|
||||
"""
|
||||
|
||||
import copy
|
||||
import httplib
|
||||
|
||||
from manila import exception
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
from manila.volume.drivers import zadara
|
||||
|
||||
from lxml import etree
|
||||
|
||||
LOG = logging.getLogger("manila.volume.driver")
|
||||
|
||||
DEFAULT_RUNTIME_VARS = {
|
||||
'status': 200,
|
||||
'user': 'test',
|
||||
'password': 'test_password',
|
||||
'access_key': '0123456789ABCDEF',
|
||||
'volumes': [],
|
||||
'servers': [],
|
||||
'controllers': [('active_ctrl', {'display_name': 'test_ctrl'})],
|
||||
'counter': 1000,
|
||||
|
||||
'login': """
|
||||
<hash>
|
||||
<user>
|
||||
<updated-at type="datetime">2012-04-30...</updated-at>
|
||||
<access-key>%s</access-key>
|
||||
<id type="integer">1</id>
|
||||
<created-at type="datetime">2012-02-21...</created-at>
|
||||
<email>jsmith@example.com</email>
|
||||
<username>jsmith</username>
|
||||
</user>
|
||||
<status type="integer">0</status>
|
||||
</hash>""",
|
||||
|
||||
'good': """
|
||||
<hash>
|
||||
<status type="integer">0</status>
|
||||
</hash>""",
|
||||
|
||||
'bad_login': """
|
||||
<hash>
|
||||
<status type="integer">5</status>
|
||||
<status-msg>Some message...</status-msg>
|
||||
</hash>""",
|
||||
|
||||
'bad_volume': """
|
||||
<hash>
|
||||
<status type="integer">10081</status>
|
||||
<status-msg>Virtual volume xxx not found</status-msg>
|
||||
</hash>""",
|
||||
|
||||
'bad_server': """
|
||||
<hash>
|
||||
<status type="integer">10086</status>
|
||||
<status-msg>Server xxx not found</status-msg>
|
||||
</hash>""",
|
||||
|
||||
'server_created': """
|
||||
<create-server-response>
|
||||
<server-name>%s</server-name>
|
||||
<status type='integer'>0</status>
|
||||
</create-server-response>""",
|
||||
}
|
||||
|
||||
RUNTIME_VARS = None
|
||||
|
||||
|
||||
class FakeRequest(object):
|
||||
def __init__(self, method, url, body):
|
||||
self.method = method
|
||||
self.url = url
|
||||
self.body = body
|
||||
self.status = RUNTIME_VARS['status']
|
||||
|
||||
def read(self):
|
||||
ops = {'POST': [('/api/users/login.xml', self._login),
|
||||
('/api/volumes.xml', self._create_volume),
|
||||
('/api/servers.xml', self._create_server),
|
||||
('/api/servers/*/volumes.xml', self._attach),
|
||||
('/api/volumes/*/detach.xml', self._detach)],
|
||||
'DELETE': [('/api/volumes/*', self._delete)],
|
||||
'GET': [('/api/volumes.xml', self._list_volumes),
|
||||
('/api/vcontrollers.xml', self._list_controllers),
|
||||
('/api/servers.xml', self._list_servers),
|
||||
('/api/volumes/*/servers.xml',
|
||||
self._list_vol_attachments)]
|
||||
}
|
||||
|
||||
ops_list = ops[self.method]
|
||||
modified_url = self.url.split('?')[0]
|
||||
for (templ_url, func) in ops_list:
|
||||
if self._compare_url(modified_url, templ_url):
|
||||
result = func()
|
||||
return result
|
||||
|
||||
def _compare_url(self, url, template_url):
|
||||
items = url.split('/')
|
||||
titems = template_url.split('/')
|
||||
for (i, titem) in enumerate(titems):
|
||||
if titem != '*' and titem != items[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_parameters(self, data):
|
||||
items = data.split('&')
|
||||
params = {}
|
||||
for item in items:
|
||||
if item:
|
||||
(k, v) = item.split('=')
|
||||
params[k] = v
|
||||
return params
|
||||
|
||||
def _get_counter(self):
|
||||
cnt = RUNTIME_VARS['counter']
|
||||
RUNTIME_VARS['counter'] += 1
|
||||
return cnt
|
||||
|
||||
def _login(self):
|
||||
params = self._get_parameters(self.body)
|
||||
if (params['user'] == RUNTIME_VARS['user'] and
|
||||
params['password'] == RUNTIME_VARS['password']):
|
||||
return RUNTIME_VARS['login'] % RUNTIME_VARS['access_key']
|
||||
else:
|
||||
return RUNTIME_VARS['bad_login']
|
||||
|
||||
def _incorrect_access_key(self, params):
|
||||
if params['access_key'] != RUNTIME_VARS['access_key']:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _create_volume(self):
|
||||
params = self._get_parameters(self.body)
|
||||
if self._incorrect_access_key(params):
|
||||
return RUNTIME_VARS['bad_login']
|
||||
|
||||
params['attachments'] = []
|
||||
vpsa_vol = 'volume-%07d' % self._get_counter()
|
||||
RUNTIME_VARS['volumes'].append((vpsa_vol, params))
|
||||
return RUNTIME_VARS['good']
|
||||
|
||||
def _create_server(self):
|
||||
params = self._get_parameters(self.body)
|
||||
if self._incorrect_access_key(params):
|
||||
return RUNTIME_VARS['bad_login']
|
||||
|
||||
vpsa_srv = 'srv-%07d' % self._get_counter()
|
||||
RUNTIME_VARS['servers'].append((vpsa_srv, params))
|
||||
return RUNTIME_VARS['server_created'] % vpsa_srv
|
||||
|
||||
def _attach(self):
|
||||
params = self._get_parameters(self.body)
|
||||
if self._incorrect_access_key(params):
|
||||
return RUNTIME_VARS['bad_login']
|
||||
|
||||
srv = self.url.split('/')[3]
|
||||
vol = params['volume_name[]']
|
||||
|
||||
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
||||
if vol_name == vol:
|
||||
attachments = params['attachments']
|
||||
if srv in attachments:
|
||||
#already attached - ok
|
||||
return RUNTIME_VARS['good']
|
||||
else:
|
||||
attachments.append(srv)
|
||||
return RUNTIME_VARS['good']
|
||||
|
||||
return RUNTIME_VARS['bad_volume']
|
||||
|
||||
def _detach(self):
|
||||
params = self._get_parameters(self.body)
|
||||
if self._incorrect_access_key(params):
|
||||
return RUNTIME_VARS['bad_login']
|
||||
|
||||
vol = self.url.split('/')[3]
|
||||
srv = params['server_name[]']
|
||||
|
||||
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
||||
if vol_name == vol:
|
||||
attachments = params['attachments']
|
||||
if srv not in attachments:
|
||||
return RUNTIME_VARS['bad_server']
|
||||
else:
|
||||
attachments.remove(srv)
|
||||
return RUNTIME_VARS['good']
|
||||
|
||||
return RUNTIME_VARS['bad_volume']
|
||||
|
||||
def _delete(self):
|
||||
vol = self.url.split('/')[3].split('.')[0]
|
||||
|
||||
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
||||
if vol_name == vol:
|
||||
if params['attachments']:
|
||||
# there are attachments - should be volume busy error
|
||||
return RUNTIME_VARS['bad_volume']
|
||||
else:
|
||||
RUNTIME_VARS['volumes'].remove((vol_name, params))
|
||||
return RUNTIME_VARS['good']
|
||||
|
||||
return RUNTIME_VARS['bad_volume']
|
||||
|
||||
def _generate_list_resp(self, header, footer, body, lst):
|
||||
resp = header
|
||||
for (obj, params) in lst:
|
||||
resp += body % (obj, params['display_name'])
|
||||
resp += footer
|
||||
return resp
|
||||
|
||||
def _list_volumes(self):
|
||||
header = """<show-volumes-response>
|
||||
<status type='integer'>0</status>
|
||||
<volumes type='array'>"""
|
||||
footer = "</volumes></show-volumes-response>"
|
||||
body = """<volume>
|
||||
<name>%s</name>
|
||||
<display-name>%s</display-name>
|
||||
<status>Available</status>
|
||||
<virtual-capacity type='integer'>1</virtual-capacity>
|
||||
<allocated-capacity type='integer'>1</allocated-capacity>
|
||||
<raid-group-name>r5</raid-group-name>
|
||||
<cache>write-through</cache>
|
||||
<created-at type='datetime'>2012-01-28...</created-at>
|
||||
<modified-at type='datetime'>2012-01-28...</modified-at>
|
||||
</volume>"""
|
||||
return self._generate_list_resp(header,
|
||||
footer,
|
||||
body,
|
||||
RUNTIME_VARS['volumes'])
|
||||
|
||||
def _list_controllers(self):
|
||||
header = """<show-vcontrollers-response>
|
||||
<status type='integer'>0</status>
|
||||
<vcontrollers type='array'>"""
|
||||
footer = "</vcontrollers></show-vcontrollers-response>"
|
||||
body = """<vcontroller>
|
||||
<name>%s</name>
|
||||
<display-name>%s</display-name>
|
||||
<state>active</state>
|
||||
<target>iqn.2011-04.com.zadarastorage:vsa-xxx:1</target>
|
||||
<iscsi-ip>1.1.1.1</iscsi-ip>
|
||||
<mgmt-ip>1.1.1.1</mgmt-ip>
|
||||
<software-ver>0.0.09-05.1--77.7</software-ver>
|
||||
<heartbeat1>ok</heartbeat1>
|
||||
<heartbeat2>ok</heartbeat2>
|
||||
<chap-username>test_chap_user</chap-username>
|
||||
<chap-target-secret>test_chap_secret</chap-target-secret>
|
||||
</vcontroller>"""
|
||||
return self._generate_list_resp(header,
|
||||
footer,
|
||||
body,
|
||||
RUNTIME_VARS['controllers'])
|
||||
|
||||
def _list_servers(self):
|
||||
header = """<show-servers-response>
|
||||
<status type='integer'>0</status>
|
||||
<servers type='array'>"""
|
||||
footer = "</servers></show-servers-response>"
|
||||
body = """<server>
|
||||
<name>%s</name>
|
||||
<display-name>%s</display-name>
|
||||
<iqn>%s</iqn>
|
||||
<status>Active</status>
|
||||
<created-at type='datetime'>2012-01-28...</created-at>
|
||||
<modified-at type='datetime'>2012-01-28...</modified-at>
|
||||
</server>"""
|
||||
|
||||
resp = header
|
||||
for (obj, params) in RUNTIME_VARS['servers']:
|
||||
resp += body % (obj, params['display_name'], params['iqn'])
|
||||
resp += footer
|
||||
return resp
|
||||
|
||||
def _get_server_obj(self, name):
|
||||
for (srv_name, params) in RUNTIME_VARS['servers']:
|
||||
if srv_name == name:
|
||||
return params
|
||||
|
||||
def _list_vol_attachments(self):
|
||||
vol = self.url.split('/')[3]
|
||||
|
||||
header = """<show-servers-response>
|
||||
<status type="integer">0</status>
|
||||
<servers type="array">"""
|
||||
footer = "</servers></show-servers-response>"
|
||||
body = """<server>
|
||||
<name>%s</name>
|
||||
<display-name>%s</display-name>
|
||||
<iqn>%s</iqn>
|
||||
<target>iqn.2011-04.com.zadarastorage:vsa-xxx:1</target>
|
||||
<lun>0</lun>
|
||||
</server>"""
|
||||
|
||||
for (vol_name, params) in RUNTIME_VARS['volumes']:
|
||||
if vol_name == vol:
|
||||
attachments = params['attachments']
|
||||
resp = header
|
||||
for server in attachments:
|
||||
srv_params = self._get_server_obj(server)
|
||||
resp += body % (server,
|
||||
srv_params['display_name'],
|
||||
srv_params['iqn'])
|
||||
resp += footer
|
||||
return resp
|
||||
|
||||
return RUNTIME_VARS['bad_volume']
|
||||
|
||||
|
||||
class FakeHTTPConnection(object):
|
||||
"""A fake httplib.HTTPConnection for zadara volume driver tests."""
|
||||
def __init__(self, host, port, use_ssl=False):
|
||||
LOG.debug('Enter: __init__ FakeHTTPConnection')
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.use_ssl = use_ssl
|
||||
self.req = None
|
||||
|
||||
def request(self, method, url, body):
|
||||
LOG.debug('Enter: request')
|
||||
self.req = FakeRequest(method, url, body)
|
||||
|
||||
def getresponse(self):
|
||||
LOG.debug('Enter: getresponse')
|
||||
return self.req
|
||||
|
||||
def close(self):
|
||||
LOG.debug('Enter: close')
|
||||
self.req = None
|
||||
|
||||
|
||||
class FakeHTTPSConnection(FakeHTTPConnection):
|
||||
def __init__(self, host, port):
|
||||
LOG.debug('Enter: __init__ FakeHTTPSConnection')
|
||||
super(FakeHTTPSConnection, self).__init__(host, port, use_ssl=True)
|
||||
|
||||
|
||||
class ZadaraVPSADriverTestCase(test.TestCase):
|
||||
"""Test case for Zadara VPSA volume driver."""
|
||||
|
||||
def setUp(self):
|
||||
LOG.debug('Enter: setUp')
|
||||
super(ZadaraVPSADriverTestCase, self).setUp()
|
||||
self.flags(
|
||||
zadara_user='test',
|
||||
zadara_password='test_password',
|
||||
)
|
||||
global RUNTIME_VARS
|
||||
RUNTIME_VARS = copy.deepcopy(DEFAULT_RUNTIME_VARS)
|
||||
|
||||
self.driver = zadara.ZadaraVPSAISCSIDriver()
|
||||
self.stubs.Set(httplib, 'HTTPConnection', FakeHTTPConnection)
|
||||
self.stubs.Set(httplib, 'HTTPSConnection', FakeHTTPSConnection)
|
||||
self.driver.do_setup(None)
|
||||
|
||||
def tearDown(self):
|
||||
super(ZadaraVPSADriverTestCase, self).tearDown()
|
||||
|
||||
def test_create_destroy(self):
|
||||
"""Create/Delete volume."""
|
||||
volume = {'name': 'test_volume_01', 'size': 1}
|
||||
self.driver.create_volume(volume)
|
||||
self.driver.delete_volume(volume)
|
||||
|
||||
def test_create_destroy_multiple(self):
|
||||
"""Create/Delete multiple volumes."""
|
||||
self.flags(zadara_vpsa_allow_nonexistent_delete=False)
|
||||
self.driver.create_volume({'name': 'test_volume_01', 'size': 1})
|
||||
self.driver.create_volume({'name': 'test_volume_02', 'size': 2})
|
||||
self.driver.create_volume({'name': 'test_volume_03', 'size': 3})
|
||||
self.driver.delete_volume({'name': 'test_volume_02'})
|
||||
self.driver.delete_volume({'name': 'test_volume_03'})
|
||||
self.driver.delete_volume({'name': 'test_volume_01'})
|
||||
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.delete_volume,
|
||||
{'name': 'test_volume_04'})
|
||||
self.flags(zadara_vpsa_allow_nonexistent_delete=True)
|
||||
self.driver.delete_volume({'name': 'test_volume_04'})
|
||||
|
||||
def test_destroy_non_existent(self):
|
||||
"""Delete non-existent volume."""
|
||||
self.flags(zadara_vpsa_allow_nonexistent_delete=False)
|
||||
volume = {'name': 'test_volume_02', 'size': 1}
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.delete_volume,
|
||||
volume)
|
||||
self.flags(zadara_vpsa_allow_nonexistent_delete=True)
|
||||
|
||||
def test_empty_apis(self):
|
||||
"""Test empty func (for coverage only)."""
|
||||
context = None
|
||||
volume = {'name': 'test_volume_01', 'size': 1}
|
||||
self.driver.create_export(context, volume)
|
||||
self.driver.ensure_export(context, volume)
|
||||
self.driver.remove_export(context, volume)
|
||||
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.create_volume_from_snapshot,
|
||||
volume, None)
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.create_snapshot,
|
||||
None)
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.delete_snapshot,
|
||||
None)
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.local_path,
|
||||
None)
|
||||
|
||||
self.driver.check_for_setup_error()
|
||||
|
||||
def test_volume_attach_detach(self):
|
||||
"""Test volume attachment and detach."""
|
||||
volume = {'name': 'test_volume_01', 'size': 1, 'id': 123}
|
||||
connector = dict(initiator='test_iqn.1')
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
|
||||
props = self.driver.initialize_connection(volume, connector)
|
||||
self.assertEqual(props['driver_volume_type'], 'iscsi')
|
||||
data = props['data']
|
||||
self.assertEqual(data['target_portal'], '1.1.1.1:3260')
|
||||
self.assertEqual(data['target_iqn'],
|
||||
'iqn.2011-04.com.zadarastorage:vsa-xxx:1')
|
||||
self.assertEqual(data['target_lun'], '0')
|
||||
self.assertEqual(data['volume_id'], 123)
|
||||
self.assertEqual(data['auth_method'], 'CHAP')
|
||||
self.assertEqual(data['auth_username'], 'test_chap_user')
|
||||
self.assertEqual(data['auth_password'], 'test_chap_secret')
|
||||
|
||||
self.driver.terminate_connection(volume, connector)
|
||||
self.driver.delete_volume(volume)
|
||||
|
||||
def test_volume_attach_multiple_detach(self):
|
||||
"""Test multiple volume attachment and detach."""
|
||||
volume = {'name': 'test_volume_01', 'size': 1, 'id': 123}
|
||||
connector1 = dict(initiator='test_iqn.1')
|
||||
connector2 = dict(initiator='test_iqn.2')
|
||||
connector3 = dict(initiator='test_iqn.3')
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
props1 = self.driver.initialize_connection(volume, connector1)
|
||||
props2 = self.driver.initialize_connection(volume, connector2)
|
||||
props3 = self.driver.initialize_connection(volume, connector3)
|
||||
|
||||
self.driver.terminate_connection(volume, connector1)
|
||||
self.driver.terminate_connection(volume, connector3)
|
||||
self.driver.terminate_connection(volume, connector2)
|
||||
self.driver.delete_volume(volume)
|
||||
|
||||
def test_wrong_attach_params(self):
|
||||
"""Test different wrong attach scenarios."""
|
||||
volume1 = {'name': 'test_volume_01', 'size': 1, 'id': 101}
|
||||
volume2 = {'name': 'test_volume_02', 'size': 1, 'id': 102}
|
||||
volume3 = {'name': 'test_volume_03', 'size': 1, 'id': 103}
|
||||
connector1 = dict(initiator='test_iqn.1')
|
||||
connector2 = dict(initiator='test_iqn.2')
|
||||
connector3 = dict(initiator='test_iqn.3')
|
||||
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.initialize_connection,
|
||||
volume1, connector1)
|
||||
|
||||
def test_wrong_detach_params(self):
|
||||
"""Test different wrong detachment scenarios."""
|
||||
|
||||
volume1 = {'name': 'test_volume_01', 'size': 1, 'id': 101}
|
||||
volume2 = {'name': 'test_volume_02', 'size': 1, 'id': 102}
|
||||
volume3 = {'name': 'test_volume_03', 'size': 1, 'id': 103}
|
||||
connector1 = dict(initiator='test_iqn.1')
|
||||
connector2 = dict(initiator='test_iqn.2')
|
||||
connector3 = dict(initiator='test_iqn.3')
|
||||
|
||||
self.driver.create_volume(volume1)
|
||||
self.driver.create_volume(volume2)
|
||||
props1 = self.driver.initialize_connection(volume1, connector1)
|
||||
props2 = self.driver.initialize_connection(volume2, connector2)
|
||||
|
||||
self.assertRaises(exception.ZadaraServerNotFound,
|
||||
self.driver.terminate_connection,
|
||||
volume1, connector3)
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
self.driver.terminate_connection,
|
||||
volume3, connector1)
|
||||
self.assertRaises(exception.FailedCmdWithDump,
|
||||
self.driver.terminate_connection,
|
||||
volume1, connector2)
|
||||
|
||||
def test_wrong_login_reply(self):
|
||||
"""Test wrong login reply."""
|
||||
|
||||
RUNTIME_VARS['login'] = """<hash>
|
||||
<access-key>%s</access-key>
|
||||
<status type="integer">0</status>
|
||||
</hash>"""
|
||||
self.assertRaises(exception.MalformedResponse,
|
||||
self.driver.do_setup, None)
|
||||
|
||||
RUNTIME_VARS['login'] = """
|
||||
<hash>
|
||||
<user>
|
||||
<updated-at type="datetime">2012-04-30...</updated-at>
|
||||
<id type="integer">1</id>
|
||||
<created-at type="datetime">2012-02-21...</created-at>
|
||||
<email>jsmith@example.com</email>
|
||||
<username>jsmith</username>
|
||||
</user>
|
||||
<access-key>%s</access-key>
|
||||
<status type="integer">0</status>
|
||||
</hash>"""
|
||||
self.assertRaises(exception.MalformedResponse,
|
||||
self.driver.do_setup, None)
|
||||
|
||||
def test_ssl_use(self):
|
||||
"""Coverage test for SSL connection."""
|
||||
self.flags(zadara_vpsa_use_ssl=True)
|
||||
self.driver.do_setup(None)
|
||||
self.flags(zadara_vpsa_use_ssl=False)
|
||||
|
||||
def test_bad_http_response(self):
|
||||
"""Coverage test for non-good HTTP response."""
|
||||
RUNTIME_VARS['status'] = 400
|
||||
|
||||
volume = {'name': 'test_volume_01', 'size': 1}
|
||||
self.assertRaises(exception.BadHTTPResponseStatus,
|
||||
self.driver.create_volume, volume)
|
||||
|
||||
def test_delete_without_detach(self):
|
||||
"""Test volume deletion without detach."""
|
||||
|
||||
volume1 = {'name': 'test_volume_01', 'size': 1, 'id': 101}
|
||||
connector1 = dict(initiator='test_iqn.1')
|
||||
connector2 = dict(initiator='test_iqn.2')
|
||||
connector3 = dict(initiator='test_iqn.3')
|
||||
|
||||
self.driver.create_volume(volume1)
|
||||
props1 = self.driver.initialize_connection(volume1, connector1)
|
||||
props2 = self.driver.initialize_connection(volume1, connector2)
|
||||
props3 = self.driver.initialize_connection(volume1, connector3)
|
||||
|
||||
self.flags(zadara_vpsa_auto_detach_on_delete=False)
|
||||
self.assertRaises(exception.VolumeAttached,
|
||||
self.driver.delete_volume, volume1)
|
||||
|
||||
self.flags(zadara_vpsa_auto_detach_on_delete=True)
|
||||
self.driver.delete_volume(volume1)
|
||||
|
||||
def test_no_active_ctrl(self):
|
||||
|
||||
RUNTIME_VARS['controllers'] = []
|
||||
|
||||
volume = {'name': 'test_volume_01', 'size': 1, 'id': 123}
|
||||
connector = dict(initiator='test_iqn.1')
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
self.assertRaises(exception.ZadaraVPSANoActiveController,
|
||||
self.driver.initialize_connection,
|
||||
volume, connector)
|
Loading…
Reference in New Issue