diff --git a/bin/nova-all b/bin/nova-all index 1d7b2dfa..2bbc27c8 100755 --- a/bin/nova-all +++ b/bin/nova-all @@ -70,8 +70,8 @@ if __name__ == '__main__': except (Exception, SystemExit): LOG.exception(_('Failed to load %s') % mod.__name__) - for binary in ['nova-compute', 'nova-volume', - 'nova-network', 'nova-scheduler', 'nova-cert']: + for binary in ['nova-compute', 'nova-network', 'nova-scheduler', + 'nova-cert']: try: launcher.launch_server(service.Service.create(binary=binary)) except (Exception, SystemExit): diff --git a/bin/nova-api-os-volume b/bin/nova-api-os-volume deleted file mode 100755 index 7c368fce..00000000 --- a/bin/nova-api-os-volume +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# 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. - -"""Starter script for Nova OS API.""" - -import eventlet -eventlet.monkey_patch(os=False) - -import os -import sys - - -possible_topdir = os.path.normpath(os.path.join(os.path.abspath( - sys.argv[0]), os.pardir, os.pardir)) -if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): - sys.path.insert(0, possible_topdir) - - -from nova import flags -from nova.openstack.common import log as logging -from nova import service -from nova import utils - -if __name__ == '__main__': - flags.parse_args(sys.argv) - logging.setup("nova") - utils.monkey_patch() - server = service.WSGIService('osapi_volume') - service.serve(server, workers=server.workers) - service.wait() diff --git a/bin/nova-manage b/bin/nova-manage index c68ff73c..9194ff49 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -777,33 +777,6 @@ class VersionCommands(object): self.list() -class VolumeCommands(object): - """Methods for dealing with a cloud in an odd state""" - - @args('--volume', dest='volume_id', metavar='', - help='Volume ID') - def reattach(self, volume_id): - """Re-attach a volume that has previously been attached - to an instance. Typically called after a compute host - has been rebooted.""" - - if 'cinder' in FLAGS.volume_api_class: - print(_("\"nova-manage volume reattach\" only valid " - "when using nova-volume service")) - sys.exit(1) - - ctxt = context.get_admin_context() - volume = db.volume_get(ctxt, param2id(volume_id)) - - if not volume['instance_id']: - print _("volume is not attached to an instance") - return - instance = db.instance_get(ctxt, volume['instance_id']) - rpcapi = compute_rpcapi.ComputeAPI() - rpcapi.attach_volume(ctxt, instance, volume['id'], - volume['mountpoint']) - - class InstanceTypeCommands(object): """Class for managing instance types / flavors.""" @@ -1207,7 +1180,6 @@ CATEGORIES = [ ('sm', StorageManagerCommands), ('version', VersionCommands), ('vm', VmCommands), - ('volume', VolumeCommands), ('vpn', VpnCommands), ] diff --git a/bin/nova-volume b/bin/nova-volume deleted file mode 100755 index 602b332b..00000000 --- a/bin/nova-volume +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# 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. - -"""Starter script for Nova Volume.""" - -import eventlet -eventlet.monkey_patch() - -import os -import sys - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): - sys.path.insert(0, possible_topdir) - - -from nova import flags -from nova.openstack.common import log as logging -from nova import service -from nova import utils - -if __name__ == '__main__': - flags.parse_args(sys.argv) - FLAGS = flags.FLAGS - logging.setup("nova") - utils.monkey_patch() - server = service.Service.create(binary='nova-volume', - topic=FLAGS.volume_topic) - service.serve(server) - service.wait() diff --git a/bin/nova-volume-usage-audit b/bin/nova-volume-usage-audit deleted file mode 100755 index 2f7f54a4..00000000 --- a/bin/nova-volume-usage-audit +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 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. - -"""Cron script to generate usage notifications for volumes existing during - the audit period. - - Together with the notifications generated by volumes - create/delete/resize, over that time period, this allows an external - system consuming usage notification feeds to calculate volume usage - for each tenant. - - Time periods are specified as 'hour', 'month', 'day' or 'year' - - hour = previous hour. If run at 9:07am, will generate usage for 8-9am. - month = previous month. If the script is run April 1, it will generate - usages for March 1 through March 31. - day = previous day. if run on July 4th, it generates usages for July 3rd. - year = previous year. If run on Jan 1, it generates usages for - Jan 1 through Dec 31 of the previous year. -""" - -import datetime -import gettext -import os -import sys -import time -import traceback - -# If ../nova/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): - sys.path.insert(0, POSSIBLE_TOPDIR) - -gettext.install('nova', unicode=1) -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova.openstack.common import log as logging -from nova.openstack.common import rpc -from nova import utils -from nova.volume import utils as volume_utils - -FLAGS = flags.FLAGS - -if __name__ == '__main__': - admin_context = context.get_admin_context() - flags.FLAGS(sys.argv) - logging.setup("nova") - begin, end = utils.last_completed_audit_period() - print _("Starting volume usage audit") - print _("Creating usages for %s until %s") % (str(begin), str(end)) - volumes = db.volume_get_active_by_window(admin_context, - begin, - end) - print _("Found %d volumes") % len(volumes) - for volume_ref in volumes: - try: - volume_utils.notify_usage_exists( - admin_context, volume_ref) - except Exception, e: - print traceback.format_exc(e) - print _("Volume usage audit completed") diff --git a/nova/flags.py b/nova/flags.py index 3927e8f8..03e607c0 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -159,9 +159,6 @@ global_opts = [ cfg.StrOpt('scheduler_topic', default='scheduler', help='the topic scheduler nodes listen on'), - cfg.StrOpt('volume_topic', - default='volume', - help='the topic volume nodes listen on'), cfg.StrOpt('network_topic', default='network', help='the topic network nodes listen on'), @@ -169,7 +166,7 @@ global_opts = [ default=True, help='whether to rate limit the api'), cfg.ListOpt('enabled_apis', - default=['ec2', 'osapi_compute', 'osapi_volume', 'metadata'], + default=['ec2', 'osapi_compute', 'metadata'], help='a list of APIs to enable by default'), cfg.StrOpt('ec2_host', default='$my_ip', @@ -197,16 +194,6 @@ global_opts = [ 'nova.api.openstack.compute.contrib.standard_extensions' ], help='osapi compute extension to load'), - cfg.ListOpt('osapi_volume_ext_list', - default=[], - help='Specify list of extensions to load when using osapi_' - 'volume_extension option with nova.api.openstack.' - 'volume.contrib.select_extensions'), - cfg.MultiStrOpt('osapi_volume_extension', - default=[ - 'nova.api.openstack.volume.contrib.standard_extensions' - ], - help='osapi volume extension to load'), cfg.StrOpt('osapi_path', default='/v1.1/', help='the path prefix used to call the openstack api server'), @@ -281,9 +268,6 @@ global_opts = [ cfg.StrOpt('network_manager', default='nova.network.manager.VlanManager', help='full class name for the Manager for network'), - cfg.StrOpt('volume_manager', - default='nova.volume.manager.VolumeManager', - help='full class name for the Manager for volume'), cfg.StrOpt('scheduler_manager', default='nova.scheduler.manager.SchedulerManager', help='full class name for the Manager for scheduler'), @@ -382,7 +366,7 @@ global_opts = [ default='nova.network.api.API', help='The full class name of the network API class to use'), cfg.StrOpt('volume_api_class', - default='nova.volume.api.API', + default='nova.volume.cinder.API', help='The full class name of the volume API class to use'), cfg.StrOpt('security_group_handler', default='nova.network.sg.NullSecurityGroupHandler', diff --git a/nova/tests/fake_flags.py b/nova/tests/fake_flags.py index 32f34b3e..0b50d45e 100644 --- a/nova/tests/fake_flags.py +++ b/nova/tests/fake_flags.py @@ -26,7 +26,6 @@ flags.DECLARE('iscsi_num_targets', 'nova.volume.driver') flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('policy_file', 'nova.policy') -flags.DECLARE('volume_driver', 'nova.volume.manager') def set_defaults(conf): @@ -44,7 +43,6 @@ def set_defaults(conf): conf.set_default('sqlite_synchronous', False) conf.set_default('use_ipv6', True) conf.set_default('verbose', True) - conf.set_default('volume_driver', 'nova.volume.driver.FakeISCSIDriver') conf.set_default('api_paste_config', '$state_path/etc/nova/api-paste.ini') conf.set_default('rpc_response_timeout', 5) conf.set_default('rpc_cast_timeout', 5) diff --git a/nova/tests/test_iscsi.py b/nova/tests/test_iscsi.py deleted file mode 100644 index 09a6e9e8..00000000 --- a/nova/tests/test_iscsi.py +++ /dev/null @@ -1,121 +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 nova import test -from nova.volume import iscsi - - -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) - - 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): - 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 --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']) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index c84440e5..f8bc3c33 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -155,7 +155,6 @@ class LibvirtVolumeTestCase(test.TestCase): } def test_libvirt_volume_driver_serial(self): - vol_driver = volume_driver.VolumeDriver() libvirt_driver = volume.LibvirtVolumeDriver(self.fake_conn) name = 'volume-00000001' vol = {'id': 1, 'name': name} diff --git a/nova/tests/test_netapp.py b/nova/tests/test_netapp.py deleted file mode 100644 index 79a8526e..00000000 --- a/nova/tests/test_netapp.py +++ /dev/null @@ -1,1380 +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. -""" -Tests for NetApp volume driver - -""" - -import BaseHTTPServer -import httplib -import StringIO - -from lxml import etree - -from nova.openstack.common import log as logging -from nova import test -from nova.volume import netapp - - -LOG = logging.getLogger(__name__) - - -WSDL_HEADER = """ -""" - -WSDL_TYPES = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""" - -WSDL_TRAILER = """ - - -""" - -RESPONSE_PREFIX = """ -""" - -RESPONSE_SUFFIX = """""" - -APIS = ['ApiProxy', 'DatasetListInfoIterStart', 'DatasetListInfoIterNext', - 'DatasetListInfoIterEnd', 'DatasetEditBegin', 'DatasetEditCommit', - 'DatasetProvisionMember', 'DatasetRemoveMember', 'DfmAbout', - 'DpJobProgressEventListIterStart', 'DpJobProgressEventListIterNext', - 'DpJobProgressEventListIterEnd', 'DatasetMemberListInfoIterStart', - 'DatasetMemberListInfoIterNext', 'DatasetMemberListInfoIterEnd', - 'HostListInfoIterStart', 'HostListInfoIterNext', 'HostListInfoIterEnd', - 'LunListInfoIterStart', 'LunListInfoIterNext', 'LunListInfoIterEnd', - 'StorageServiceDatasetProvision'] - -iter_count = 0 -iter_table = {} - - -class FakeDfmServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - """HTTP handler that fakes enough stuff to allow the driver to run""" - - def do_GET(s): - """Respond to a GET request.""" - if '/dfm.wsdl' != s.path: - s.send_response(404) - s.end_headers - return - s.send_response(200) - s.send_header("Content-Type", "application/wsdl+xml") - s.end_headers() - out = s.wfile - out.write(WSDL_HEADER) - out.write(WSDL_TYPES) - for api in APIS: - out.write('' % api) - out.write('' % api) - out.write('') - out.write('' % api) - out.write('' % api) - out.write('') - out.write('') - for api in APIS: - out.write('' % api) - out.write('' % api) - out.write('' % api) - out.write('') - out.write('') - out.write('') - out.write('') - for api in APIS: - out.write('' % api) - out.write('' % api) - out.write('') - out.write('') - out.write('') - out.write('') - out.write(WSDL_TRAILER) - - def do_POST(s): - """Respond to a POST request.""" - if '/apis/soap/v1' != s.path: - s.send_response(404) - s.end_headers - return - request_xml = s.rfile.read(int(s.headers['Content-Length'])) - ntap_ns = 'http://www.netapp.com/management/v1' - nsmap = {'env': 'http://schemas.xmlsoap.org/soap/envelope/', - 'na': ntap_ns} - root = etree.fromstring(request_xml) - - body = root.xpath('/env:Envelope/env:Body', namespaces=nsmap)[0] - request = body.getchildren()[0] - tag = request.tag - if not tag.startswith('{' + ntap_ns + '}'): - s.send_response(500) - s.end_headers - return - api = tag[(2 + len(ntap_ns)):] - global iter_count - global iter_table - if 'DatasetListInfoIterStart' == api: - iter_name = 'dataset_%s' % iter_count - iter_count = iter_count + 1 - iter_table[iter_name] = 0 - body = """ - 1 - %s - """ % iter_name - elif 'DatasetListInfoIterNext' == api: - tags = body.xpath('na:DatasetListInfoIterNext/na:Tag', - namespaces=nsmap) - iter_name = tags[0].text - if iter_table[iter_name]: - body = """ - - 0 - """ - else: - iter_table[iter_name] = 1 - body = """ - - - 0 - - - OpenStackProject - testproj - - - OpenStackVolType - - - - OpenStack_testproj - - - 1 - """ - elif 'DatasetListInfoIterEnd' == api: - body = """""" - elif 'DatasetEditBegin' == api: - body = """ - 0 - """ - elif 'DatasetEditCommit' == api: - body = """ - false - - - 0 - - - """ - elif 'DatasetProvisionMember' == api: - body = """""" - elif 'DatasetRemoveMember' == api: - body = """""" - elif 'DfmAbout' == api: - body = """""" - elif 'DpJobProgressEventListIterStart' == api: - iter_name = 'dpjobprogress_%s' % iter_count - iter_count = iter_count + 1 - iter_table[iter_name] = 0 - body = """ - 2 - %s - """ % iter_name - elif 'DpJobProgressEventListIterNext' == api: - tags = body.xpath('na:DpJobProgressEventListIterNext/na:Tag', - namespaces=nsmap) - iter_name = tags[0].text - if iter_table[iter_name]: - body = """""" - else: - iter_table[iter_name] = 1 - name = ('filer:/OpenStack_testproj/volume-00000001/' - 'volume-00000001') - body = """ - - - normal - lun-create - - 0 - %s - - - - normal - job-end - - - 2 - """ % name - elif 'DpJobProgressEventListIterEnd' == api: - body = """""" - elif 'DatasetMemberListInfoIterStart' == api: - iter_name = 'datasetmember_%s' % iter_count - iter_count = iter_count + 1 - iter_table[iter_name] = 0 - body = """ - 1 - %s - """ % iter_name - elif 'DatasetMemberListInfoIterNext' == api: - tags = body.xpath('na:DatasetMemberListInfoIterNext/na:Tag', - namespaces=nsmap) - iter_name = tags[0].text - if iter_table[iter_name]: - body = """ - - 0 - """ - else: - iter_table[iter_name] = 1 - name = ('filer:/OpenStack_testproj/volume-00000001/' - 'volume-00000001') - body = """ - - - 0 - %s - - - 1 - """ % name - elif 'DatasetMemberListInfoIterEnd' == api: - body = """""" - elif 'HostListInfoIterStart' == api: - body = """ - 1 - host - """ - elif 'HostListInfoIterNext' == api: - body = """ - - - 1.2.3.4 - 0 - filer - - - 1 - """ - elif 'HostListInfoIterEnd' == api: - body = """""" - elif 'LunListInfoIterStart' == api: - body = """ - 1 - lun - """ - elif 'LunListInfoIterNext' == api: - path = 'OpenStack_testproj/volume-00000001/volume-00000001' - body = """ - - - 0 - %s - - - 1 - """ % path - elif 'LunListInfoIterEnd' == api: - body = """""" - elif 'ApiProxy' == api: - names = body.xpath('na:ApiProxy/na:Request/na:Name', - namespaces=nsmap) - proxy = names[0].text - if 'igroup-list-info' == proxy: - igroup = 'openstack-iqn.1993-08.org.debian:01:23456789' - initiator = 'iqn.1993-08.org.debian:01:23456789' - proxy_body = """ - - %s - iscsi - linux - - - %s - - - - """ % (igroup, initiator) - elif 'igroup-create' == proxy: - proxy_body = '' - elif 'igroup-add' == proxy: - proxy_body = '' - elif 'lun-map-list-info' == proxy: - proxy_body = '' - elif 'lun-map' == proxy: - proxy_body = '0' - elif 'lun-unmap' == proxy: - proxy_body = '' - elif 'iscsi-portal-list-info' == proxy: - proxy_body = """ - - 1.2.3.4 - 3260 - 1000 - - """ - elif 'iscsi-node-get-name' == proxy: - target = 'iqn.1992-08.com.netapp:sn.111111111' - proxy_body = '%s' % target - else: - # Unknown proxy API - s.send_response(500) - s.end_headers - return - api = api + ':' + proxy - proxy_header = '' - proxy_trailer = """passed - """ - body = proxy_header + proxy_body + proxy_trailer - else: - # Unknown API - s.send_response(500) - s.end_headers - return - s.send_response(200) - s.send_header("Content-Type", "text/xml; charset=utf-8") - s.end_headers() - s.wfile.write(RESPONSE_PREFIX) - s.wfile.write(body) - s.wfile.write(RESPONSE_SUFFIX) - - -class FakeHttplibSocket(object): - """A fake socket implementation for httplib.HTTPResponse""" - def __init__(self, value): - self._rbuffer = StringIO.StringIO(value) - self._wbuffer = StringIO.StringIO('') - oldclose = self._wbuffer.close - - def newclose(): - self.result = self._wbuffer.getvalue() - oldclose() - self._wbuffer.close = newclose - - def makefile(self, mode, _other): - """Returns the socket's internal buffer""" - if mode == 'r' or mode == 'rb': - return self._rbuffer - if mode == 'w' or mode == 'wb': - return self._wbuffer - - -class FakeHTTPConnection(object): - """A fake httplib.HTTPConnection for netapp tests - - Requests made via this connection actually get translated and routed into - the fake Dfm handler above, we then turn the response into - the httplib.HTTPResponse that the caller expects. - """ - def __init__(self, host, timeout=None): - self.host = host - - def request(self, method, path, data=None, headers=None): - if not headers: - headers = {} - req_str = '%s %s HTTP/1.1\r\n' % (method, path) - for key, value in headers.iteritems(): - req_str += "%s: %s\r\n" % (key, value) - if data: - req_str += '\r\n%s' % data - - # NOTE(vish): normally the http transport normailizes from unicode - sock = FakeHttplibSocket(req_str.decode("latin-1").encode("utf-8")) - # NOTE(vish): stop the server from trying to look up address from - # the fake socket - FakeDfmServerHandler.address_string = lambda x: '127.0.0.1' - self.app = FakeDfmServerHandler(sock, '127.0.0.1:8088', None) - - self.sock = FakeHttplibSocket(sock.result) - self.http_response = httplib.HTTPResponse(self.sock) - - def set_debuglevel(self, level): - pass - - def getresponse(self): - self.http_response.begin() - return self.http_response - - def getresponsebody(self): - return self.sock.result - - -class NetAppDriverTestCase(test.TestCase): - """Test case for NetAppISCSIDriver""" - STORAGE_SERVICE = 'Openstack Service' - STORAGE_SERVICE_PREFIX = 'Openstack Service-' - PROJECT_ID = 'testproj' - VOLUME_NAME = 'volume-00000001' - VOLUME_TYPE = '' - VOLUME_SIZE = 2147483648L # 2 GB - INITIATOR = 'iqn.1993-08.org.debian:01:23456789' - - def setUp(self): - super(NetAppDriverTestCase, self).setUp() - driver = netapp.NetAppISCSIDriver() - self.stubs.Set(httplib, 'HTTPConnection', FakeHTTPConnection) - driver._create_client(wsdl_url='http://localhost:8088/dfm.wsdl', - login='root', password='password', - hostname='localhost', port=8088, cache=False) - driver._set_storage_service(self.STORAGE_SERVICE) - driver._set_storage_service_prefix(self.STORAGE_SERVICE_PREFIX) - driver._set_vfiler('') - self.driver = driver - - def test_connect(self): - self.driver.check_for_setup_error() - - def test_create_destroy(self): - self.driver._discover_luns() - self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID, - self.VOLUME_TYPE, self.VOLUME_SIZE) - self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID) - - def test_map_unmap(self): - self.driver._discover_luns() - self.driver._provision(self.VOLUME_NAME, None, self.PROJECT_ID, - self.VOLUME_TYPE, self.VOLUME_SIZE) - volume = {'name': self.VOLUME_NAME, 'project_id': self.PROJECT_ID, - 'id': 0, 'provider_auth': None} - updates = self.driver._get_export(volume) - self.assertTrue(updates['provider_location']) - volume['provider_location'] = updates['provider_location'] - connector = {'initiator': self.INITIATOR} - connection_info = self.driver.initialize_connection(volume, connector) - self.assertEqual(connection_info['driver_volume_type'], 'iscsi') - properties = connection_info['data'] - self.driver.terminate_connection(volume, connector) - self.driver._remove_destroy(self.VOLUME_NAME, self.PROJECT_ID) - - -WSDL_HEADER_CMODE = """ - -""" - -WSDL_TYPES_CMODE = """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - """ - -WSDL_TRAILER_CMODE = """ - - - - -""" - -RESPONSE_PREFIX_CMODE = """ - -""" - -RESPONSE_SUFFIX_CMODE = """""" - -CMODE_APIS = ['ProvisionLun', 'DestroyLun', 'CloneLun', 'MapLun', 'UnmapLun', - 'ListLuns', 'GetLunTargetDetails'] - - -class FakeCMODEServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - """HTTP handler that fakes enough stuff to allow the driver to run""" - - def do_GET(s): - """Respond to a GET request.""" - if '/ntap_cloud.wsdl' != s.path: - s.send_response(404) - s.end_headers - return - s.send_response(200) - s.send_header("Content-Type", "application/wsdl+xml") - s.end_headers() - out = s.wfile - out.write(WSDL_HEADER_CMODE) - out.write(WSDL_TYPES_CMODE) - for api in CMODE_APIS: - out.write('' % api) - out.write('' % api) - out.write('') - out.write('' % api) - out.write('' % api) - out.write('') - out.write('') - for api in CMODE_APIS: - out.write('' % api) - out.write('' % api) - out.write('' % api) - out.write('') - out.write('') - out.write('') - out.write('') - for api in CMODE_APIS: - out.write('' % api) - out.write('') - out.write('') - out.write('') - out.write('') - out.write('') - out.write(WSDL_TRAILER_CMODE) - - def do_POST(s): - """Respond to a POST request.""" - if '/ws/ntapcloud' != s.path: - s.send_response(404) - s.end_headers - return - request_xml = s.rfile.read(int(s.headers['Content-Length'])) - ntap_ns = 'http://cloud.netapp.com/' - nsmap = {'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', - 'na': ntap_ns} - root = etree.fromstring(request_xml) - - body = root.xpath('/soapenv:Envelope/soapenv:Body', - namespaces=nsmap)[0] - request = body.getchildren()[0] - tag = request.tag - if not tag.startswith('{' + ntap_ns + '}'): - s.send_response(500) - s.end_headers - return - api = tag[(2 + len(ntap_ns)):] - if 'ProvisionLun' == api: - body = """ - lun120 - 1d9c006c-a406-42f6-a23f-5ed7a6dc33e3 - OsType - linux - """ - elif 'DestroyLun' == api: - body = """""" - elif 'CloneLun' == api: - body = """ - lun22 - 98ea1791d228453899d422b4611642c3 - OsType - linux - """ - elif 'MapLun' == api: - body = """""" - elif 'Unmap' == api: - body = """""" - elif 'ListLuns' == api: - body = """ - - lun1 - 20 - asdjdnsd - - """ - elif 'GetLunTargetDetails' == api: - body = """ - -
1.2.3.4
- 3260 - 1000 - iqn.199208.com.netapp:sn.123456789 - 0 -
-
""" - else: - # Unknown API - s.send_response(500) - s.end_headers - return - s.send_response(200) - s.send_header("Content-Type", "text/xml; charset=utf-8") - s.end_headers() - s.wfile.write(RESPONSE_PREFIX_CMODE) - s.wfile.write(body) - s.wfile.write(RESPONSE_SUFFIX_CMODE) - - -class FakeCmodeHTTPConnection(object): - """A fake httplib.HTTPConnection for netapp tests - - Requests made via this connection actually get translated and routed into - the fake Dfm handler above, we then turn the response into - the httplib.HTTPResponse that the caller expects. - """ - def __init__(self, host, timeout=None): - self.host = host - - def request(self, method, path, data=None, headers=None): - if not headers: - headers = {} - req_str = '%s %s HTTP/1.1\r\n' % (method, path) - for key, value in headers.iteritems(): - req_str += "%s: %s\r\n" % (key, value) - if data: - req_str += '\r\n%s' % data - - # NOTE(vish): normally the http transport normailizes from unicode - sock = FakeHttplibSocket(req_str.decode("latin-1").encode("utf-8")) - # NOTE(vish): stop the server from trying to look up address from - # the fake socket - FakeCMODEServerHandler.address_string = lambda x: '127.0.0.1' - self.app = FakeCMODEServerHandler(sock, '127.0.0.1:8080', None) - - self.sock = FakeHttplibSocket(sock.result) - self.http_response = httplib.HTTPResponse(self.sock) - - def set_debuglevel(self, level): - pass - - def getresponse(self): - self.http_response.begin() - return self.http_response - - def getresponsebody(self): - return self.sock.result - - -class NetAppCmodeISCSIDriverTestCase(test.TestCase): - """Test case for NetAppISCSIDriver""" - volume = { - 'name': 'lun1', 'size': 1, 'volume_name': 'lun1', - 'os_type': 'linux', 'provider_location': 'lun1', - 'id': 'lun1', 'provider_auth': None, 'project_id': 'project', - 'display_name': None, 'display_description': 'lun1', - 'volume_type_id': None - } - snapshot = { - 'name': 'lun2', 'size': 1, 'volume_name': 'lun1', - 'volume_size': 1, 'project_id': 'project' - } - volume_sec = { - 'name': 'vol_snapshot', 'size': 1, 'volume_name': 'lun1', - 'os_type': 'linux', 'provider_location': 'lun1', - 'id': 'lun1', 'provider_auth': None, 'project_id': 'project', - 'display_name': None, 'display_description': 'lun1', - 'volume_type_id': None - } - - def setUp(self): - super(NetAppCmodeISCSIDriverTestCase, self).setUp() - driver = netapp.NetAppCmodeISCSIDriver() - self.stubs.Set(httplib, 'HTTPConnection', FakeCmodeHTTPConnection) - driver._create_client(wsdl_url='http://localhost:8080/ntap_cloud.wsdl', - login='root', password='password', - hostname='localhost', port=8080, cache=False) - self.driver = driver - - def test_connect(self): - self.driver.check_for_setup_error() - - def test_create_destroy(self): - self.driver.create_volume(self.volume) - self.driver.delete_volume(self.volume) - - def test_create_vol_snapshot_destroy(self): - self.driver.create_volume(self.volume) - self.driver.create_snapshot(self.snapshot) - self.driver.create_volume_from_snapshot(self.volume_sec, self.snapshot) - self.driver.delete_snapshot(self.snapshot) - self.driver.delete_volume(self.volume) - - def test_map_unmap(self): - self.driver.create_volume(self.volume) - updates = self.driver.create_export(None, self.volume) - self.assertTrue(updates['provider_location']) - self.volume['provider_location'] = updates['provider_location'] - connector = {'initiator': 'init1'} - connection_info = self.driver.initialize_connection(self.volume, - connector) - self.assertEqual(connection_info['driver_volume_type'], 'iscsi') - properties = connection_info['data'] - self.driver.terminate_connection(self.volume, connector) - self.driver.delete_volume(self.volume) diff --git a/nova/tests/test_netapp_nfs.py b/nova/tests/test_netapp_nfs.py deleted file mode 100644 index 1a882438..00000000 --- a/nova/tests/test_netapp_nfs.py +++ /dev/null @@ -1,234 +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 nova import context -from nova import exception -from nova import test - -from nova.volume import netapp -from nova.volume import netapp_nfs -from nova.volume import nfs - -from mox import IgnoreArg -from mox import IsA -from mox import MockObject - -import mox -import suds -import types - - -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._driver = netapp_nfs.NetAppNFSDriver() - super(NetappNfsDriverTestCase, self).setUp() - - def test_check_for_setup_error(self): - mox = self.mox - drv = self._driver - - # check exception raises when flags are not set - self.assertRaises(exception.NovaException, - drv.check_for_setup_error) - - # set required flags - self.flags(netapp_wsdl_url='val', - netapp_login='val', - netapp_password='val', - netapp_server_hostname='val', - netapp_server_port='val') - - mox.StubOutWithMock(nfs.NfsDriver, 'check_for_setup_error') - nfs.NfsDriver.check_for_setup_error() - mox.ReplayAll() - - drv.check_for_setup_error() - - def test_do_setup(self): - mox = self.mox - drv = self._driver - - mox.StubOutWithMock(drv, 'check_for_setup_error') - mox.StubOutWithMock(netapp_nfs.NetAppNFSDriver, '_get_client') - - drv.check_for_setup_error() - netapp_nfs.NetAppNFSDriver._get_client() - - mox.ReplayAll() - - drv.do_setup(IsA(context.RequestContext)) - - 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()) - - 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.NovaException, - 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) - - 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 - self._prepare_delete_snapshot_mock(True) - - drv.delete_snapshot(FakeSnapshot()) - - def test_delete_missing_snapshot(self): - drv = self._driver - self._prepare_delete_snapshot_mock(False) - - drv.delete_snapshot(FakeSnapshot()) - - 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') - - 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) - - 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.NovaException, - drv._clone_volume, - volume_name, clone_name, volume_id) diff --git a/nova/tests/test_nexenta.py b/nova/tests/test_nexenta.py deleted file mode 100644 index aac877cc..00000000 --- a/nova/tests/test_nexenta.py +++ /dev/null @@ -1,278 +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 Nova volume driver -""" - -import base64 -import urllib2 - -import nova.flags -import nova.test -from nova.volume import nexenta -from nova.volume.nexenta import jsonrpc -from nova.volume.nexenta import volume - -FLAGS = nova.flags.FLAGS - - -class TestNexentaDriver(nova.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 setUp(self): - super(TestNexentaDriver, self).setUp() - self.flags( - nexenta_host='1.1.1.1', - nexenta_volume='nova', - nexenta_target_prefix='iqn:', - nexenta_target_group_prefix='nova/', - 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('nova').AndReturn(True) - self.mox.ReplayAll() - self.drv.check_for_setup_error() - - def test_setup_error_fail(self): - self.nms_mock.volume.object_exists('nova').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('nova/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('nova/volume1', '') - self.mox.ReplayAll() - self.drv.delete_volume(self.TEST_VOLUME_REF) - - def test_create_snapshot(self): - self.nms_mock.zvol.create_snapshot('nova/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('nova/volume1@snapshot1', 'nova/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('nova/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:nova-volume1 already' - u' configured\n' - u' itadm create-target failed with error 17\n', - ), - ('stmf', 'create_targetgroup', ('nova/volume1',), - u'Unable to create targetgroup: stmfadm: nova/volume1:' - u' already exists\n', - ), - ('stmf', 'add_targetgroup_member', ('nova/volume1', 'iqn:volume1'), - u'Unable to add member to targetgroup: stmfadm:' - u' iqn.1986-03.com.sun:02:nova-volume1: already exists\n', - ), - ('scsidisk', 'create_lu', ('nova/volume1', {}), - u"Unable to create lu with zvol 'nova/volume1':\n" - u" sbdadm: filename /dev/zvol/rdsk/nova/volume1: in use\n", - ), - ('scsidisk', 'add_lun_mapping_entry', ('nova/volume1', { - 'target_group': 'nova/volume1', 'lun': '0'}), - u"Unable to add view to zvol 'nova/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' % (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('nova/volume1') - self.nms_mock.stmf.destroy_targetgroup('nova/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('nova/volume1') - self.nms_mock.stmf.destroy_targetgroup('nova/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('nova/volume1') - self.nms_mock.stmf.destroy_targetgroup('nova/volume1') - self.nms_mock.iscsitarget.delete_target('iqn:volume1').AndRaise( - nexenta.NexentaException()) - self.mox.ReplayAll() - self.drv.remove_export({}, self.TEST_VOLUME_REF) - - -class TestNexentaJSONRPC(nova.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') diff --git a/nova/tests/test_nfs.py b/nova/tests/test_nfs.py deleted file mode 100644 index d0d235b1..00000000 --- a/nova/tests/test_nfs.py +++ /dev/null @@ -1,569 +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 - -import mox as mox_lib -from mox import IgnoreArg -from mox import IsA -from mox import stubout - -from nova import context -from nova import exception -from nova.exception import ProcessExecutionError -from nova import test - -from nova.volume import nfs - - -class DumbVolume(object): - fields = {} - - def __setitem__(self, key, value): - self.fields[key] = value - - def __getitem__(self, item): - return self.fields[item] - - -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/cinder/test-shares.conf' - ONE_GB_IN_BYTES = 1024 * 1024 * 1024 - - def setUp(self): - self._driver = nfs.NfsDriver() - super(NfsDriverTestCase, self).setUp() - - 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_path_exists_should_return_true(self): - """_path_exists should return True if stat returns 0""" - mox = self.mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('stat', self.TEST_FILE_NAME, run_as_root=True) - - mox.ReplayAll() - - self.assertTrue(drv._path_exists(self.TEST_FILE_NAME)) - - def test_path_exists_should_return_false(self): - """_path_exists should return True if stat doesn't return 0""" - mox = self.mox - drv = self._driver - - mox.StubOutWithMock(drv, '_execute') - drv._execute('stat', self.TEST_FILE_NAME, run_as_root=True).\ - AndRaise(ProcessExecutionError( - stderr="stat: cannot stat `test.txt': No such file or directory")) - - mox.ReplayAll() - - self.assertFalse(drv._path_exists(self.TEST_FILE_NAME)) - - def test_local_path(self): - """local_path common use case""" - self.flags(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, '_path_exists') - drv._path_exists(self.TEST_MNT_POINT).AndReturn(True) - - mox.StubOutWithMock(drv, '_execute') - 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) - - 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, '_path_exists') - drv._path_exists(self.TEST_MNT_POINT).AndReturn(True) - - mox.StubOutWithMock(drv, '_execute') - 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) - - 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, '_path_exists') - drv._path_exists(self.TEST_MNT_POINT).AndReturn(True) - - mox.StubOutWithMock(drv, '_execute') - 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) - - 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, '_path_exists') - drv._path_exists(self.TEST_MNT_POINT).AndReturn(False) - - 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) - - 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, '_path_exists') - drv._path_exists(self.TEST_MNT_POINT).AndReturn(True) - - mox.StubOutWithMock(drv, '_execute') - drv._execute(*([IgnoreArg()] * 5), run_as_root=IgnoreArg()) - - mox.ReplayAll() - - drv._mount_nfs(self.TEST_NFS_EXPORT1, self.TEST_MNT_POINT) - - 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.flags(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_avail = 1490560 - df_head = 'Filesystem 1K-blocks Used Available Use% Mounted on\n' - df_data = 'nfs-host:/export 2620544 996864 %d 41%% /mnt' % df_avail - df_output = df_head + df_data - - self.flags(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, - drv._get_available_capacity(self.TEST_NFS_EXPORT1)) - - def test_get_available_capacity_with_du(self): - """_get_available_capacity should calculate correct value""" - mox = self.mox - drv = self._driver - - self.flags(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, - drv._get_available_capacity(self.TEST_NFS_EXPORT1)) - - def test_load_shares_config(self): - mox = self.mox - drv = self._driver - - self.flags(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) - - 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) - - 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]) - - 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)) - - 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.flags(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.flags(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)) - - 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) - drv._get_available_capacity(self.TEST_NFS_EXPORT2).\ - AndReturn(3 * self.ONE_GB_IN_BYTES) - - mox.ReplayAll() - - self.assertEqual(self.TEST_NFS_EXPORT2, - drv._find_share(self.TEST_SIZE_IN_GB)) - - 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) - drv._get_available_capacity(self.TEST_NFS_EXPORT2).\ - AndReturn(0) - - mox.ReplayAll() - - self.assertRaises(exception.NfsNoSuitableShareFound, drv._find_share, - self.TEST_SIZE_IN_GB) - - 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() - - self.flags(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) - - def test_create_nonsparsed_volume(self): - mox = self.mox - drv = self._driver - volume = self._simple_volume() - - self.flags(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) - - def test_create_volume_should_ensure_nfs_mounted(self): - """create_volume should ensure 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) - - 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']) - - 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, '_path_exists') - drv._path_exists(self.TEST_LOCAL_PATH).AndReturn(True) - - mox.StubOutWithMock(drv, '_execute') - drv._execute('rm', '-f', self.TEST_LOCAL_PATH, run_as_root=True) - - mox.ReplayAll() - - drv.delete_volume(volume) - - 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) - - def test_delete_should_not_delete_if_provider_location_not_provided(self): - """delete_volume shouldn't try to 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) - - def test_delete_should_not_delete_if_there_is_no_file(self): - """delete_volume should not try to delete if file 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'] = self.TEST_NFS_EXPORT1 - - mox.StubOutWithMock(drv, 'local_path') - drv.local_path(volume).AndReturn(self.TEST_LOCAL_PATH) - - mox.StubOutWithMock(drv, '_path_exists') - drv._path_exists(self.TEST_LOCAL_PATH).AndReturn(False) - - mox.StubOutWithMock(drv, '_execute') - - mox.ReplayAll() - - drv.delete_volume(volume) diff --git a/nova/tests/test_plugin_api_extensions.py b/nova/tests/test_plugin_api_extensions.py index af30c10d..a40dd327 100644 --- a/nova/tests/test_plugin_api_extensions.py +++ b/nova/tests/test_plugin_api_extensions.py @@ -72,7 +72,6 @@ class APITestCase(test.TestCase): # Marking out the default extension paths makes this test MUCH faster. self.flags(osapi_compute_extension=[]) - self.flags(osapi_volume_extension=[]) found = False mgr = computeextensions.ExtensionManager() diff --git a/nova/tests/test_rbd.py b/nova/tests/test_rbd.py deleted file mode 100644 index 8e90f3ae..00000000 --- a/nova/tests/test_rbd.py +++ /dev/null @@ -1,161 +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. - -from nova import db -from nova import exception -from nova.openstack.common import log as logging -from nova.openstack.common import timeutils -from nova import test -from nova.tests.image import fake as fake_image -from nova.tests.test_volume import DriverTestCase -from nova.volume.driver import RBDDriver - -LOG = logging.getLogger(__name__) - - -class RBDTestCase(test.TestCase): - - def setUp(self): - super(RBDTestCase, self).setUp() - - def fake_execute(*args): - pass - self.driver = RBDDriver(execute=fake_execute) - - 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)) - - -class FakeRBDDriver(RBDDriver): - - def _clone(self): - pass - - def _resize(self): - pass - - -class ManagedRBDTestCase(DriverTestCase): - driver_name = "nova.tests.test_rbd.FakeRBDDriver" - - 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): - pass - - def fake_clone_error(volume, image_location): - raise exception.NovaException() - - 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.NovaException, - 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')) diff --git a/nova/tests/test_solidfire.py b/nova/tests/test_solidfire.py deleted file mode 100644 index 87a211da..00000000 --- a/nova/tests/test_solidfire.py +++ /dev/null @@ -1,208 +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. - -from nova import exception -from nova.openstack.common import log as logging -from nova import test -from nova.volume.solidfire import SolidFire - -LOG = logging.getLogger(__name__) - - -class SolidFireVolumeTestCase(test.TestCase): - def fake_issue_api_request(obj, method, params): - if method is 'GetClusterInfo': - LOG.info('Called Fake GetClusterInfo...') - results = {'result': {'clusterInfo': - {'name': 'fake-cluster', - 'mvip': '1.1.1.1', - 'svip': '1.1.1.1', - 'uniqueID': 'unqid', - 'repCount': 2, - 'attributes': {}}}} - return results - - elif method is 'AddAccount': - LOG.info('Called Fake AddAccount...') - return {'result': {'accountID': 25}, 'id': 1} - - elif method is 'GetAccountByName': - LOG.info('Called Fake GetAccountByName...') - results = {'result': {'account': - {'accountID': 25, - 'username': params['username'], - 'status': 'active', - 'initiatorSecret': '123456789012', - 'targetSecret': '123456789012', - 'attributes': {}, - 'volumes': [6, 7, 20]}}, - "id": 1} - return results - - elif method is 'CreateVolume': - LOG.info('Called Fake CreateVolume...') - return {'result': {'volumeID': 5}, 'id': 1} - - elif method is 'DeleteVolume': - LOG.info('Called Fake DeleteVolume...') - return {'result': {}, 'id': 1} - - elif method is 'ListVolumesForAccount': - 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_no_volume(obj, method, params): - if method is 'ListVolumesForAccount': - LOG.info('Called Fake ListVolumesForAccount...') - return {'result': {'volumes': []}} - else: - return obj.fake_issue_api_request(method, params) - - 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_volume_get(obj, key, default=None): - return {'qos': 'fast'} - - 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'} - sfv = SolidFire() - model_update = sfv.create_volume(testvol) - - 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]} - - sfv = SolidFire() - model_update = sfv.create_volume(testvol) - - def test_create_volume_fails(self): - 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() - self.assertRaises(exception.SolidFireAPIDataException, - sfv.create_volume, testvol) - - def test_create_sfaccount(self): - sfv = SolidFire() - 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() - 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() - 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() - 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() - model_update = sfv.delete_volume(testvol) - - def test_delete_volume_fails_no_volume(self): - self.stubs.Set(SolidFire, '_issue_api_request', - self.fake_issue_api_request_no_volume) - testvol = {'project_id': 'testprjid', - 'name': 'no-name', - 'size': 1, - 'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'} - sfv = SolidFire() - self.assertRaises(exception.VolumeNotFound, - sfv.delete_volume, testvol) - - def test_delete_volume_fails_account_lookup(self): - 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() - 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() - sfv._get_cluster_info() - - def test_get_cluster_info_fail(self): - self.stubs.Set(SolidFire, '_issue_api_request', - self.fake_issue_api_request_fails) - sfv = SolidFire() - self.assertRaises(exception.SolidFireAPIException, - sfv._get_cluster_info) diff --git a/nova/tests/test_storwize_svc.py b/nova/tests/test_storwize_svc.py deleted file mode 100644 index 51304325..00000000 --- a/nova/tests/test_storwize_svc.py +++ /dev/null @@ -1,1376 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2012 IBM, 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. -# -# Authors: -# Ronen Kat -# Avishay Traeger - -""" -Tests for the IBM Storwize V7000 and SVC volume driver. -""" - -import random -import socket - -from nova import exception -from nova import flags -from nova.openstack.common import excutils -from nova.openstack.common import log as logging -from nova import test -from nova.volume import storwize_svc - -FLAGS = flags.FLAGS - -LOG = logging.getLogger(__name__) - - -class StorwizeSVCManagementSimulator: - def __init__(self, pool_name): - self._flags = {"storwize_svc_volpool_name": pool_name} - self._volumes_list = {} - self._hosts_list = {} - self._mappings_list = {} - self._fcmappings_list = {} - self._next_cmd_error = { - "lsportip": "", - "lsnodecanister": "", - "mkvdisk": "", - "lsvdisk": "", - "lsfcmap": "", - "prestartfcmap": "", - "startfcmap": "", - "rmfcmap": "", - } - self._errors = { - "CMMVC5701E": ("", "CMMVC5701E No object ID was specified."), - "CMMVC6035E": ("", "CMMVC6035E The action failed as the " + - "object already exists."), - "CMMVC5753E": ("", "CMMVC5753E The specified object does not " + - "exist or is not a suitable candidate."), - "CMMVC5707E": ("", "CMMVC5707E Required parameters are missing."), - "CMMVC6581E": ("", "CMMVC6581E The command has failed because " + - "the maximum number of allowed iSCSI " + - "qualified names (IQNs) has been reached, " + - "or the IQN is already assigned or is not " + - "valid."), - "CMMVC5754E": ("", "CMMVC5754E The specified object does not " + - "exist, or the name supplied does not meet " + - "the naming rules."), - "CMMVC6071E": ("", "CMMVC6071E The VDisk-to-host mapping was " + - "not created because the VDisk is already " + - "mapped to a host."), - "CMMVC5879E": ("", "CMMVC5879E The VDisk-to-host mapping was " + - "not created because a VDisk is already " + - "mapped to this host with this SCSI LUN."), - "CMMVC5840E": ("", "CMMVC5840E The virtual disk (VDisk) was " + - "not deleted because it is mapped to a " + - "host or because it is part of a FlashCopy " + - "or Remote Copy mapping, or is involved in " + - "an image mode migrate."), - "CMMVC6527E": ("", "CMMVC6527E The name that you have entered " + - "is not valid. The name can contain letters, " + - "numbers, spaces, periods, dashes, and " + - "underscores. The name must begin with a " + - "letter or an underscore. The name must not " + - "begin or end with a space."), - "CMMVC5871E": ("", "CMMVC5871E The action failed because one or " + - "more of the configured port names is in a " + - "mapping."), - "CMMVC5924E": ("", "CMMVC5924E The FlashCopy mapping was not " + - "created because the source and target " + - "virtual disks (VDisks) are different sizes."), - "CMMVC6303E": ("", "CMMVC6303E The create failed because the " + - "source and target VDisks are the same."), - "CMMVC7050E": ("", "CMMVC7050E The command failed because at " + - "least one node in the I/O group does not " + - "support compressed VDisks."), - } - - # Find an unused ID - def _find_unused_id(self, d): - ids = [] - for k, v in d.iteritems(): - ids.append(int(v["id"])) - ids.sort() - for index, n in enumerate(ids): - if n > index: - return str(index) - return str(len(ids)) - - # Check if name is valid - def _is_invalid_name(self, name): - if (name[0] == " ") or (name[-1] == " "): - return True - for c in name: - if ((not c.isalnum()) and (c != " ") and (c != ".") - and (c != "-") and (c != "_")): - return True - return False - - # Convert argument string to dictionary - def _cmd_to_dict(self, cmd): - arg_list = cmd.split() - no_param_args = [ - "autodelete", - "autoexpand", - "bytes", - "compressed", - "force", - "nohdr", - ] - one_param_args = [ - "cleanrate", - "delim", - "filtervalue", - "grainsize", - "host", - "iogrp", - "iscsiname", - "mdiskgrp", - "name", - "rsize", - "scsi", - "size", - "source", - "target", - "unit", - "easytier", - "warning", - ] - - # Handle the special case of lsnode which is a two-word command - # Use the one word version of the command internally - if arg_list[0] == "svcinfo" and arg_list[1] == "lsnode": - ret = {"cmd": "lsnodecanister"} - arg_list.pop(0) - else: - ret = {"cmd": arg_list[0]} - - skip = False - for i in range(1, len(arg_list)): - if skip: - skip = False - continue - if arg_list[i][0] == "-": - if arg_list[i][1:] in no_param_args: - ret[arg_list[i][1:]] = True - elif arg_list[i][1:] in one_param_args: - ret[arg_list[i][1:]] = arg_list[i + 1] - skip = True - else: - raise exception.InvalidInput( - reason=_('unrecognized argument %s') % arg_list[i]) - else: - ret["obj"] = arg_list[i] - return ret - - # Generic function for printing information - def _print_info_cmd(self, rows, delim=" ", nohdr=False, **kwargs): - if nohdr: - del rows[0] - - for index in range(len(rows)): - rows[index] = delim.join(rows[index]) - return ("%s" % "\n".join(rows), "") - - # Print mostly made-up stuff in the correct syntax - def _cmd_lsmdiskgrp(self, **kwargs): - rows = [None] * 3 - rows[0] = ["id", "name", "status", "mdisk_count", - "vdisk_count capacity", "extent_size", "free_capacity", - "virtual_capacity", "used_capacity", "real_capacity", - "overallocation", "warning", "easy_tier", - "easy_tier_status"] - rows[1] = ["1", self._flags["storwize_svc_volpool_name"], "online", - "1", str(len(self._volumes_list)), "3.25TB", "256", - "3.21TB", "1.54TB", "264.97MB", "35.58GB", "47", "80", - "auto", "inactive"] - rows[2] = ["2", "volpool2", "online", - "1", "0", "3.25TB", "256", - "3.21TB", "1.54TB", "264.97MB", "35.58GB", "47", "80", - "auto", "inactive"] - return self._print_info_cmd(rows=rows, **kwargs) - - # Print mostly made-up stuff in the correct syntax - def _cmd_lsnodecanister(self, **kwargs): - rows = [None] * 3 - rows[0] = ["id", "name", "UPS_serial_number", "WWNN", "status", - "IO_group_id", "IO_group_name", "config_node", - "UPS_unique_id", "hardware", "iscsi_name", "iscsi_alias", - "panel_name", "enclosure_id", "canister_id", - "enclosure_serial_number"] - rows[1] = ["5", "node1", "", "123456789ABCDEF0", "online", "0", - "io_grp0", - "yes", "123456789ABCDEF0", "100", - "iqn.1982-01.com.ibm:1234.sim.node1", "", "01-1", "1", "1", - "0123ABC"] - rows[2] = ["6", "node2", "", "123456789ABCDEF1", "online", "0", - "io_grp0", - "no", "123456789ABCDEF1", "100", - "iqn.1982-01.com.ibm:1234.sim.node2", "", "01-2", "1", "2", - "0123ABC"] - - if self._next_cmd_error["lsnodecanister"] == "header_mismatch": - rows[0].pop(2) - self._next_cmd_error["lsnodecanister"] = "" - if self._next_cmd_error["lsnodecanister"] == "remove_field": - for row in rows: - row.pop(0) - self._next_cmd_error["lsnodecanister"] = "" - - return self._print_info_cmd(rows=rows, **kwargs) - - # Print mostly made-up stuff in the correct syntax - def _cmd_lsportip(self, **kwargs): - if self._next_cmd_error["lsportip"] == "ip_no_config": - self._next_cmd_error["lsportip"] = "" - ip_addr1 = "" - ip_addr2 = "" - gw = "" - else: - ip_addr1 = "1.234.56.78" - ip_addr2 = "1.234.56.79" - gw = "1.234.56.1" - - rows = [None] * 17 - rows[0] = ["id", "node_id", "node_name", "IP_address", "mask", - "gateway", "IP_address_6", "prefix_6", "gateway_6", "MAC", - "duplex", "state", "speed", "failover"] - rows[1] = ["1", "5", "node1", ip_addr1, "255.255.255.0", - gw, "", "", "", "01:23:45:67:89:00", "Full", - "online", "1Gb/s", "no"] - rows[2] = ["1", "5", "node1", "", "", "", "", "", "", - "01:23:45:67:89:00", "Full", "online", "1Gb/s", "yes"] - rows[3] = ["2", "5", "node1", "", "", "", "", "", "", - "01:23:45:67:89:01", "Full", "unconfigured", "1Gb/s", "no"] - rows[4] = ["2", "5", "node1", "", "", "", "", "", "", - "01:23:45:67:89:01", "Full", "unconfigured", "1Gb/s", "yes"] - rows[5] = ["3", "5", "node1", "", "", "", "", "", "", "", "", - "unconfigured", "", "no"] - rows[6] = ["3", "5", "node1", "", "", "", "", "", "", "", "", - "unconfigured", "", "yes"] - rows[7] = ["4", "5", "node1", "", "", "", "", "", "", "", "", - "unconfigured", "", "no"] - rows[8] = ["4", "5", "node1", "", "", "", "", "", "", "", "", - "unconfigured", "", "yes"] - rows[9] = ["1", "6", "node2", ip_addr2, "255.255.255.0", - gw, "", "", "", "01:23:45:67:89:02", "Full", - "online", "1Gb/s", "no"] - rows[10] = ["1", "6", "node2", "", "", "", "", "", "", - "01:23:45:67:89:02", "Full", "online", "1Gb/s", "yes"] - rows[11] = ["2", "6", "node2", "", "", "", "", "", "", - "01:23:45:67:89:03", "Full", "unconfigured", "1Gb/s", "no"] - rows[12] = ["2", "6", "node2", "", "", "", "", "", "", - "01:23:45:67:89:03", "Full", "unconfigured", "1Gb/s", - "yes"] - rows[13] = ["3", "6", "node2", "", "", "", "", "", "", "", "", - "unconfigured", "", "no"] - rows[14] = ["3", "6", "node2", "", "", "", "", "", "", "", "", - "unconfigured", "", "yes"] - rows[15] = ["4", "6", "node2", "", "", "", "", "", "", "", "", - "unconfigured", "", "no"] - rows[16] = ["4", "6", "node2", "", "", "", "", "", "", "", "", - "unconfigured", "", "yes"] - - if self._next_cmd_error["lsportip"] == "header_mismatch": - rows[0].pop(2) - self._next_cmd_error["lsportip"] = "" - if self._next_cmd_error["lsportip"] == "remove_field": - for row in rows: - row.pop(1) - self._next_cmd_error["lsportip"] = "" - - return self._print_info_cmd(rows=rows, **kwargs) - - # Create a vdisk - def _cmd_mkvdisk(self, **kwargs): - # We only save the id/uid, name, and size - all else will be made up - volume_info = {} - volume_info["id"] = self._find_unused_id(self._volumes_list) - volume_info["uid"] = ("ABCDEF" * 3) + ("0" * 14) + volume_info["id"] - - if "name" in kwargs: - volume_info["name"] = kwargs["name"].strip('\'\"') - else: - volume_info["name"] = "vdisk" + volume_info["id"] - - # Assume size and unit are given, store it in bytes - capacity = int(kwargs["size"]) - unit = kwargs["unit"] - - if unit == "b": - cap_bytes = capacity - elif unit == "kb": - cap_bytes = capacity * pow(1024, 1) - elif unit == "mb": - cap_bytes = capacity * pow(1024, 2) - elif unit == "gb": - cap_bytes = capacity * pow(1024, 3) - elif unit == "tb": - cap_bytes = capacity * pow(1024, 4) - elif unit == "pb": - cap_bytes = capacity * pow(1024, 5) - volume_info["cap_bytes"] = str(cap_bytes) - volume_info["capacity"] = str(capacity) + unit.upper() - - if "easytier" in kwargs: - if kwargs["easytier"] == "on": - volume_info["easy_tier"] = "on" - else: - volume_info["easy_tier"] = "off" - - if "rsize" in kwargs: - # Fake numbers - volume_info["used_capacity"] = "0.75MB" - volume_info["real_capacity"] = "36.98MB" - volume_info["free_capacity"] = "36.23MB" - volume_info["used_capacity_bytes"] = "786432" - volume_info["real_capacity_bytes"] = "38776340" - volume_info["free_capacity_bytes"] = "37989908" - if "warning" in kwargs: - volume_info["warning"] = kwargs["warning"].rstrip('%') - else: - volume_info["warning"] = "80" - if "autoexpand" in kwargs: - volume_info["autoexpand"] = "on" - else: - volume_info["autoexpand"] = "off" - if "grainsize" in kwargs: - volume_info["grainsize"] = kwargs["grainsize"] - else: - volume_info["grainsize"] = "32" - if "compressed" in kwargs: - if self._next_cmd_error["mkvdisk"] == "no_compression": - self._next_cmd_error["mkvdisk"] = "" - return self._errors["CMMVC7050E"] - volume_info["compressed_copy"] = "yes" - else: - volume_info["compressed_copy"] = "no" - else: - volume_info["used_capacity"] = volume_info["capacity"] - volume_info["real_capacity"] = volume_info["capacity"] - volume_info["free_capacity"] = "0.00MB" - volume_info["used_capacity_bytes"] = volume_info["cap_bytes"] - volume_info["real_capacity_bytes"] = volume_info["cap_bytes"] - volume_info["free_capacity_bytes"] = "0" - volume_info["warning"] = "" - volume_info["autoexpand"] = "" - volume_info["grainsize"] = "" - volume_info["compressed_copy"] = "no" - - if volume_info["name"] in self._volumes_list: - return self._errors["CMMVC6035E"] - else: - self._volumes_list[volume_info["name"]] = volume_info - return ("Virtual Disk, id [%s], successfully created" % - (volume_info["id"]), "") - - # Delete a vdisk - def _cmd_rmvdisk(self, **kwargs): - force = 0 - if "force" in kwargs: - force = 1 - - if "obj" not in kwargs: - return self._errors["CMMVC5701E"] - vol_name = kwargs["obj"].strip('\'\"') - - if not vol_name in self._volumes_list: - return self._errors["CMMVC5753E"] - - if force == 0: - for k, mapping in self._mappings_list.iteritems(): - if mapping["vol"] == vol_name: - return self._errors["CMMVC5840E"] - for k, fcmap in self._fcmappings_list.iteritems(): - if ((fcmap["source"] == vol_name) or - (fcmap["target"] == vol_name)): - return self._errors["CMMVC5840E"] - - del self._volumes_list[vol_name] - return ("", "") - - def _get_fcmap_info(self, vol_name): - ret_vals = { - "fc_id": "", - "fc_name": "", - "fc_map_count": "0", - } - for k, fcmap in self._fcmappings_list.iteritems(): - if ((fcmap["source"] == vol_name) or - (fcmap["target"] == vol_name)): - ret_vals["fc_id"] = fcmap["id"] - ret_vals["fc_name"] = fcmap["name"] - ret_vals["fc_map_count"] = "1" - return ret_vals - - # List information about vdisks - def _cmd_lsvdisk(self, **kwargs): - if "obj" not in kwargs: - rows = [] - rows.append(["id", "name", "IO_group_id", "IO_group_name", - "status", "mdisk_grp_id", "mdisk_grp_name", - "capacity", "type", "FC_id", "FC_name", "RC_id", - "RC_name", "vdisk_UID", "fc_map_count", "copy_count", - "fast_write_state", "se_copy_count", "RC_change"]) - - for k, vol in self._volumes_list.iteritems(): - if (("filtervalue" not in kwargs) or - (kwargs["filtervalue"] == "name=" + vol["name"])): - fcmap_info = self._get_fcmap_info(vol["name"]) - - if "bytes" in kwargs: - cap = vol["cap_bytes"] - else: - cap = vol["capacity"] - rows.append([str(vol["id"]), vol["name"], "0", "io_grp0", - "online", "0", - self._flags["storwize_svc_volpool_name"], - cap, "striped", - fcmap_info["fc_id"], fcmap_info["fc_name"], - "", "", vol["uid"], - fcmap_info["fc_map_count"], "1", "empty", - "1", "no"]) - - return self._print_info_cmd(rows=rows, **kwargs) - - else: - if kwargs["obj"] not in self._volumes_list: - return self._errors["CMMVC5754E"] - vol = self._volumes_list[kwargs["obj"]] - fcmap_info = self._get_fcmap_info(vol["name"]) - if "bytes" in kwargs: - cap = vol["cap_bytes"] - cap_u = vol["used_capacity_bytes"] - cap_r = vol["real_capacity_bytes"] - cap_f = vol["free_capacity_bytes"] - else: - cap = vol["capacity"] - cap_u = vol["used_capacity"] - cap_r = vol["real_capacity"] - cap_f = vol["free_capacity"] - rows = [] - - rows.append(["id", str(vol["id"])]) - rows.append(["name", vol["name"]]) - rows.append(["IO_group_id", "0"]) - rows.append(["IO_group_name", "io_grp0"]) - rows.append(["status", "online"]) - rows.append(["mdisk_grp_id", "0"]) - rows.append(["mdisk_grp_name", - self._flags["storwize_svc_volpool_name"]]) - rows.append(["capacity", cap]) - rows.append(["type", "striped"]) - rows.append(["formatted", "no"]) - rows.append(["mdisk_id", ""]) - rows.append(["mdisk_name", ""]) - rows.append(["FC_id", fcmap_info["fc_id"]]) - rows.append(["FC_name", fcmap_info["fc_name"]]) - rows.append(["RC_id", ""]) - rows.append(["RC_name", ""]) - rows.append(["vdisk_UID", vol["uid"]]) - rows.append(["throttling", "0"]) - - if self._next_cmd_error["lsvdisk"] == "blank_pref_node": - rows.append(["preferred_node_id", ""]) - self._next_cmd_error["lsvdisk"] = "" - elif self._next_cmd_error["lsvdisk"] == "no_pref_node": - self._next_cmd_error["lsvdisk"] = "" - else: - rows.append(["preferred_node_id", "6"]) - rows.append(["fast_write_state", "empty"]) - rows.append(["cache", "readwrite"]) - rows.append(["udid", ""]) - rows.append(["fc_map_count", fcmap_info["fc_map_count"]]) - rows.append(["sync_rate", "50"]) - rows.append(["copy_count", "1"]) - rows.append(["se_copy_count", "0"]) - rows.append(["mirror_write_priority", "latency"]) - rows.append(["RC_change", "no"]) - rows.append(["used_capacity", cap_u]) - rows.append(["real_capacity", cap_r]) - rows.append(["free_capacity", cap_f]) - rows.append(["autoexpand", vol["autoexpand"]]) - rows.append(["warning", vol["warning"]]) - rows.append(["grainsize", vol["grainsize"]]) - rows.append(["easy_tier", vol["easy_tier"]]) - rows.append(["compressed_copy", vol["compressed_copy"]]) - - if "nohdr" in kwargs: - for index in range(len(rows)): - rows[index] = " ".join(rows[index][1:]) - - if "delim" in kwargs: - for index in range(len(rows)): - rows[index] = kwargs["delim"].join(rows[index]) - - return ("%s" % "\n".join(rows), "") - - # Make a host - def _cmd_mkhost(self, **kwargs): - host_info = {} - host_info["id"] = self._find_unused_id(self._hosts_list) - - if "name" in kwargs: - host_name = kwargs["name"].strip('\'\"') - else: - host_name = "host" + str(host_info["id"]) - host_info["host_name"] = host_name - - if "iscsiname" not in kwargs: - return self._errors["CMMVC5707E"] - host_info["iscsi_name"] = kwargs["iscsiname"].strip('\'\"') - - if self._is_invalid_name(host_name): - return self._errors["CMMVC6527E"] - - if host_name in self._hosts_list: - return self._errors["CMMVC6035E"] - - for k, v in self._hosts_list.iteritems(): - if v["iscsi_name"] == host_info["iscsi_name"]: - return self._errors["CMMVC6581E"] - - self._hosts_list[host_name] = host_info - return ("Host, id [%s], successfully created" % - (host_info["id"]), "") - - # Remove a host - def _cmd_rmhost(self, **kwargs): - if "obj" not in kwargs: - return self._errors["CMMVC5701E"] - - host_name = kwargs["obj"].strip('\'\"') - if host_name not in self._hosts_list: - return self._errors["CMMVC5753E"] - - for k, v in self._mappings_list.iteritems(): - if (v["host"] == host_name): - return self._errors["CMMVC5871E"] - - del self._hosts_list[host_name] - return ("", "") - - # List information about hosts - def _cmd_lshost(self, **kwargs): - if "obj" not in kwargs: - rows = [] - rows.append(["id", "name", "port_count", "iogrp_count", "status"]) - - found = False - for k, host in self._hosts_list.iteritems(): - filterstr = "name=" + host["host_name"] - if (("filtervalue" not in kwargs) or - (kwargs["filtervalue"] == filterstr)): - rows.append([host["id"], host["host_name"], "1", "4", - "offline"]) - found = True - if found: - return self._print_info_cmd(rows=rows, **kwargs) - else: - return ("", "") - else: - if kwargs["obj"] not in self._hosts_list: - return self._errors["CMMVC5754E"] - host = self._hosts_list[kwargs["obj"]] - rows = [] - rows.append(["id", host["id"]]) - rows.append(["name", host["host_name"]]) - rows.append(["port_count", "1"]) - rows.append(["type", "generic"]) - rows.append(["mask", "1111"]) - rows.append(["iogrp_count", "4"]) - rows.append(["status", "offline"]) - rows.append(["iscsi_name", host["iscsi_name"]]) - rows.append(["node_logged_in_count", "0"]) - rows.append(["state", "offline"]) - - if "nohdr" in kwargs: - for index in range(len(rows)): - rows[index] = " ".join(rows[index][1:]) - - if "delim" in kwargs: - for index in range(len(rows)): - rows[index] = kwargs["delim"].join(rows[index]) - - return ("%s" % "\n".join(rows), "") - - # Create a vdisk-host mapping - def _cmd_mkvdiskhostmap(self, **kwargs): - mapping_info = {} - mapping_info["id"] = self._find_unused_id(self._mappings_list) - - if "host" not in kwargs: - return self._errors["CMMVC5707E"] - mapping_info["host"] = kwargs["host"].strip('\'\"') - - if "scsi" not in kwargs: - return self._errors["CMMVC5707E"] - mapping_info["lun"] = kwargs["scsi"].strip('\'\"') - - if "obj" not in kwargs: - return self._errors["CMMVC5707E"] - mapping_info["vol"] = kwargs["obj"].strip('\'\"') - - if not mapping_info["vol"] in self._volumes_list: - return self._errors["CMMVC5753E"] - - if not mapping_info["host"] in self._hosts_list: - return self._errors["CMMVC5754E"] - - if mapping_info["vol"] in self._mappings_list: - return self._errors["CMMVC6071E"] - - for k, v in self._mappings_list.iteritems(): - if ((v["host"] == mapping_info["host"]) and - (v["lun"] == mapping_info["lun"])): - return self._errors["CMMVC5879E"] - - self._mappings_list[mapping_info["vol"]] = mapping_info - return ("Virtual Disk to Host map, id [%s], successfully created" - % (mapping_info["id"]), "") - - # Delete a vdisk-host mapping - def _cmd_rmvdiskhostmap(self, **kwargs): - if "host" not in kwargs: - return self._errors["CMMVC5707E"] - host = kwargs["host"].strip('\'\"') - - if "obj" not in kwargs: - return self._errors["CMMVC5701E"] - vol = kwargs["obj"].strip('\'\"') - - if not vol in self._mappings_list: - return self._errors["CMMVC5753E"] - - if self._mappings_list[vol]["host"] != host: - return self._errors["CMMVC5753E"] - - del self._mappings_list[vol] - return ("", "") - - # List information about vdisk-host mappings - def _cmd_lshostvdiskmap(self, **kwargs): - index = 1 - no_hdr = 0 - delimeter = "" - host_name = kwargs["obj"] - - if host_name not in self._hosts_list: - return self._errors["CMMVC5754E"] - - rows = [] - rows.append(["id", "name", "SCSI_id", "vdisk_id", "vdisk_name", - "vdisk_UID"]) - - for k, mapping in self._mappings_list.iteritems(): - if (host_name == "") or (mapping["host"] == host_name): - volume = self._volumes_list[mapping["vol"]] - rows.append([mapping["id"], mapping["host"], - mapping["lun"], volume["id"], - volume["name"], volume["uid"]]) - - return self._print_info_cmd(rows=rows, **kwargs) - - # Create a FlashCopy mapping - def _cmd_mkfcmap(self, **kwargs): - source = "" - target = "" - - if "source" not in kwargs: - return self._errors["CMMVC5707E"] - source = kwargs["source"].strip('\'\"') - if not source in self._volumes_list: - return self._errors["CMMVC5754E"] - - if "target" not in kwargs: - return self._errors["CMMVC5707E"] - target = kwargs["target"].strip('\'\"') - if not target in self._volumes_list: - return self._errors["CMMVC5754E"] - - if source == target: - return self._errors["CMMVC6303E"] - - if (self._volumes_list[source]["cap_bytes"] != - self._volumes_list[target]["cap_bytes"]): - return self._errors["CMMVC5924E"] - - fcmap_info = {} - fcmap_info["source"] = source - fcmap_info["target"] = target - fcmap_info["id"] = self._find_unused_id(self._fcmappings_list) - fcmap_info["name"] = "fcmap" + fcmap_info["id"] - fcmap_info["status"] = "idle_or_copied" - fcmap_info["progress"] = "0" - self._fcmappings_list[target] = fcmap_info - - return("FlashCopy Mapping, id [" + fcmap_info["id"] + - "], successfully created", "") - - # Same function used for both prestartfcmap and startfcmap - def _cmd_gen_startfcmap(self, mode, **kwargs): - if "obj" not in kwargs: - return self._errors["CMMVC5701E"] - id_num = kwargs["obj"] - - if mode == "pre": - if self._next_cmd_error["prestartfcmap"] == "bad_id": - id_num = -1 - self._next_cmd_error["prestartfcmap"] = "" - else: - if self._next_cmd_error["startfcmap"] == "bad_id": - id_num = -1 - self._next_cmd_error["startfcmap"] = "" - - for k, fcmap in self._fcmappings_list.iteritems(): - if fcmap["id"] == id_num: - if mode == "pre": - fcmap["status"] = "preparing" - else: - fcmap["status"] = "copying" - fcmap["progress"] = "0" - return ("", "") - return self._errors["CMMVC5753E"] - - # Same function used for both stopfcmap and rmfcmap - # Assumes it is called with "-force " - def _cmd_stoprmfcmap(self, mode, **kwargs): - if "obj" not in kwargs: - return self._errors["CMMVC5701E"] - id_num = kwargs["obj"] - - if self._next_cmd_error["rmfcmap"] == "bad_id": - id_num = -1 - self._next_cmd_error["rmfcmap"] = "" - - to_delete = None - found = False - for k, fcmap in self._fcmappings_list.iteritems(): - if fcmap["id"] == id_num: - found = True - if mode == "rm": - to_delete = k - - if to_delete: - del self._fcmappings_list[to_delete] - - if found: - return ("", "") - else: - return self._errors["CMMVC5753E"] - - def _cmd_lsfcmap(self, **kwargs): - rows = [] - rows.append(["id", "name", "source_vdisk_id", "source_vdisk_name", - "target_vdisk_id", "target_vdisk_name", "group_id", - "group_name", "status", "progress", "copy_rate", - "clean_progress", "incremental", "partner_FC_id", - "partner_FC_name", "restoring", "start_time", - "rc_controlled"]) - - # Assume we always get a filtervalue argument - filter_key = kwargs["filtervalue"].split("=")[0] - filter_value = kwargs["filtervalue"].split("=")[1] - to_delete = [] - for k, v in self._fcmappings_list.iteritems(): - if str(v[filter_key]) == filter_value: - source = self._volumes_list[v["source"]] - target = self._volumes_list[v["target"]] - old_status = v["status"] - if old_status == "preparing": - new_status = "prepared" - if self._next_cmd_error["lsfcmap"] == "bogus_prepare": - new_status = "bogus" - elif (old_status == "copying") and (v["progress"] == "0"): - new_status = "copying" - v["progress"] = "50" - elif (old_status == "copying") and (v["progress"] == "50"): - new_status = "idle_or_copied" - to_delete.append(k) - else: - new_status = old_status - v["status"] = new_status - - if ((self._next_cmd_error["lsfcmap"] == "speed_up") or - (self._next_cmd_error["lsfcmap"] == "bogus_prepare")): - print_status = new_status - self._next_cmd_error["lsfcmap"] = "" - else: - print_status = old_status - - rows.append([v["id"], v["name"], source["id"], - source["name"], target["id"], target["name"], "", - "", print_status, v["progress"], "50", "100", - "off", "", "", "no", "", "no"]) - - for d in to_delete: - del self._fcmappings_list[k] - - return self._print_info_cmd(rows=rows, **kwargs) - - # The main function to run commands on the management simulator - def execute_command(self, cmd, check_exit_code=True): - try: - kwargs = self._cmd_to_dict(cmd) - except IndexError: - return self._errors["CMMVC5707E"] - - command = kwargs["cmd"] - del kwargs["cmd"] - arg_list = cmd.split() - - if command == "lsmdiskgrp": - out, err = self._cmd_lsmdiskgrp(**kwargs) - elif command == "lsnodecanister": - out, err = self._cmd_lsnodecanister(**kwargs) - elif command == "lsportip": - out, err = self._cmd_lsportip(**kwargs) - elif command == "mkvdisk": - out, err = self._cmd_mkvdisk(**kwargs) - elif command == "rmvdisk": - out, err = self._cmd_rmvdisk(**kwargs) - elif command == "lsvdisk": - out, err = self._cmd_lsvdisk(**kwargs) - elif command == "mkhost": - out, err = self._cmd_mkhost(**kwargs) - elif command == "rmhost": - out, err = self._cmd_rmhost(**kwargs) - elif command == "lshost": - out, err = self._cmd_lshost(**kwargs) - elif command == "mkvdiskhostmap": - out, err = self._cmd_mkvdiskhostmap(**kwargs) - elif command == "rmvdiskhostmap": - out, err = self._cmd_rmvdiskhostmap(**kwargs) - elif command == "lshostvdiskmap": - out, err = self._cmd_lshostvdiskmap(**kwargs) - elif command == "mkfcmap": - out, err = self._cmd_mkfcmap(**kwargs) - elif command == "prestartfcmap": - out, err = self._cmd_gen_startfcmap(mode="pre", **kwargs) - elif command == "startfcmap": - out, err = self._cmd_gen_startfcmap(mode="start", **kwargs) - elif command == "stopfcmap": - out, err = self._cmd_stoprmfcmap(mode="stop", **kwargs) - elif command == "rmfcmap": - out, err = self._cmd_stoprmfcmap(mode="rm", **kwargs) - elif command == "lsfcmap": - out, err = self._cmd_lsfcmap(**kwargs) - else: - out, err = ("", "ERROR: Unsupported command") - - if (check_exit_code) and (len(err) != 0): - raise exception.ProcessExecutionError(exit_code=1, - stdout=out, - stderr=err, - cmd=' '.join(cmd)) - - return (out, err) - - # After calling this function, the next call to the specified command will - # result in in the error specified - def error_injection(self, cmd, error): - self._next_cmd_error[cmd] = error - - -class StorwizeSVCFakeDriver(storwize_svc.StorwizeSVCDriver): - def set_fake_storage(self, fake): - self.fake_storage = fake - - def _run_ssh(self, cmd, check_exit_code=True): - try: - LOG.debug(_('Run CLI command: %s') % cmd) - ret = self.fake_storage.execute_command(cmd, check_exit_code) - (stdout, stderr) = ret - LOG.debug(_('CLI output:\n stdout: %(out)s\n stderr: %(err)s') % - {'out': stdout, 'err': stderr}) - - except exception.ProcessExecutionError as e: - with excutils.save_and_reraise_exception(): - LOG.debug(_('CLI Exception output:\n stdout: %(out)s\n ' - 'stderr: %(err)s') % {'out': e.stdout, - 'err': e.stderr}) - - return ret - - -class StorwizeSVCDriverTestCase(test.TestCase): - def setUp(self): - super(StorwizeSVCDriverTestCase, self).setUp() - self.USESIM = 1 - if self.USESIM == 1: - self.flags( - san_ip="hostname", - san_login="user", - san_password="pass", - storwize_svc_flashcopy_timeout="20", - ) - self.sim = StorwizeSVCManagementSimulator("volpool") - self.driver = StorwizeSVCFakeDriver() - self.driver.set_fake_storage(self.sim) - else: - self.flags( - san_ip="-1.-1.-1.-1", - san_login="user", - san_password="password", - storwize_svc_volpool_name="pool", - ) - self.driver = storwize_svc.StorwizeSVCDriver() - - self.driver.do_setup(None) - self.driver.check_for_setup_error() - - def test_storwize_svc_volume_tests(self): - self.flags(storwize_svc_vol_rsize="-1") - volume = {} - volume["name"] = "test1_volume%s" % random.randint(10000, 99999) - volume["size"] = 10 - volume["id"] = 1 - self.driver.create_volume(volume) - # Make sure that the volume has been created - is_volume_defined = self.driver._is_volume_defined(volume["name"]) - self.assertEqual(is_volume_defined, True) - self.driver.delete_volume(volume) - - if self.USESIM == 1: - self.flags(storwize_svc_vol_rsize="2%") - self.flags(storwize_svc_vol_compression=True) - self.driver.create_volume(volume) - is_volume_defined = self.driver._is_volume_defined(volume["name"]) - self.assertEqual(is_volume_defined, True) - self.driver.delete_volume(volume) - FLAGS.reset() - - def test_storwize_svc_ip_connectivity(self): - # Check for missing san_ip - self.flags(san_ip=None) - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - - if self.USESIM != 1: - # Check for invalid ip - self.flags(san_ip="-1.-1.-1.-1") - self.assertRaises(socket.gaierror, - self.driver.check_for_setup_error) - - # Check for unreachable IP - self.flags(san_ip="1.1.1.1") - self.assertRaises(socket.error, - self.driver.check_for_setup_error) - - def test_storwize_svc_connectivity(self): - # Make sure we detect if the pool doesn't exist - no_exist_pool = "i-dont-exist-%s" % random.randint(10000, 99999) - self.flags(storwize_svc_volpool_name=no_exist_pool) - self.assertRaises(exception.InvalidInput, - self.driver.check_for_setup_error) - FLAGS.reset() - - # Check the case where the user didn't configure IP addresses - # as well as receiving unexpected results from the storage - if self.USESIM == 1: - self.sim.error_injection("lsnodecanister", "header_mismatch") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - self.sim.error_injection("lsnodecanister", "remove_field") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - self.sim.error_injection("lsportip", "ip_no_config") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - self.sim.error_injection("lsportip", "header_mismatch") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - self.sim.error_injection("lsportip", "remove_field") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.check_for_setup_error) - - # Check with bad parameters - self.flags(san_password=None) - self.flags(san_private_key=None) - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_vol_rsize="invalid") - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_vol_warning="invalid") - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_vol_autoexpand="invalid") - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_vol_grainsize=str(42)) - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_flashcopy_timeout=str(601)) - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - self.flags(storwize_svc_vol_compression=True) - self.flags(storwize_svc_vol_rsize="-1") - self.assertRaises(exception.InvalidInput, - self.driver._check_flags) - FLAGS.reset() - - # Finally, check with good parameters - self.driver.check_for_setup_error() - - def test_storwize_svc_flashcopy(self): - volume1 = {} - volume1["name"] = "test1_volume%s" % random.randint(10000, 99999) - volume1["size"] = 10 - volume1["id"] = 10 - self.driver.create_volume(volume1) - - snapshot = {} - snapshot["name"] = "snap_volume%s" % random.randint(10000, 99999) - snapshot["volume_name"] = volume1["name"] - - # Test timeout and volume cleanup - self.flags(storwize_svc_flashcopy_timeout=str(1)) - self.assertRaises(exception.InvalidSnapshot, - self.driver.create_snapshot, snapshot) - is_volume_defined = self.driver._is_volume_defined(snapshot["name"]) - self.assertEqual(is_volume_defined, False) - FLAGS.reset() - - # Test bogus statuses - if self.USESIM == 1: - self.sim.error_injection("lsfcmap", "bogus_prepare") - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.create_snapshot, snapshot) - - # Test prestartfcmap, startfcmap, and rmfcmap failing - if self.USESIM == 1: - self.sim.error_injection("prestartfcmap", "bad_id") - self.assertRaises(exception.ProcessExecutionError, - self.driver.create_snapshot, snapshot) - self.sim.error_injection("lsfcmap", "speed_up") - self.sim.error_injection("startfcmap", "bad_id") - self.assertRaises(exception.ProcessExecutionError, - self.driver.create_snapshot, snapshot) - self.sim.error_injection("prestartfcmap", "bad_id") - self.sim.error_injection("rmfcmap", "bad_id") - self.assertRaises(exception.ProcessExecutionError, - self.driver.create_snapshot, snapshot) - - # Test successful snapshot - self.driver.create_snapshot(snapshot) - - # Ensure snapshot is defined - is_volume_defined = self.driver._is_volume_defined(snapshot["name"]) - self.assertEqual(is_volume_defined, True) - - # Try to create a snapshot from an non-existing volume - should fail - snapshot2 = {} - snapshot2["name"] = "snap_volume%s" % random.randint(10000, 99999) - snapshot2["volume_name"] = "undefined-vol" - self.assertRaises(exception.VolumeNotFound, - self.driver.create_snapshot, - snapshot2) - - # Create volume from snapshot - volume2 = {} - volume2["name"] = "snap2vol_volume%s" % random.randint(10000, 99999) - - # Create volume from snapshot into an existsing volume - self.assertRaises(exception.InvalidSnapshot, - self.driver.create_volume_from_snapshot, - volume1, - snapshot) - - # Try to create a volume from a non-existing snapshot - self.assertRaises(exception.SnapshotNotFound, - self.driver.create_volume_from_snapshot, - volume2, - snapshot2) - - # Fail the snapshot - if self.USESIM == 1: - self.sim.error_injection("prestartfcmap", "bad_id") - self.assertRaises(exception.ProcessExecutionError, - self.driver.create_volume_from_snapshot, volume2, snapshot) - - # Succeed - if self.USESIM == 1: - self.sim.error_injection("lsfcmap", "speed_up") - self.driver.create_volume_from_snapshot(volume2, snapshot) - - # Ensure volume is defined - is_volume_defined = self.driver._is_volume_defined(volume2["name"]) - self.assertEqual(is_volume_defined, True) - - self.driver._delete_volume(volume2, True) - self.driver._delete_snapshot(snapshot, True) - - # Check with target with different size - volume3 = {} - volume3["name"] = "test3_volume%s" % random.randint(10000, 99999) - volume3["size"] = 11 - volume3["id"] = 11 - self.driver.create_volume(volume3) - snapshot["name"] = volume3["name"] - self.assertRaises(exception.InvalidSnapshot, - self.driver.create_snapshot, snapshot) - self.driver._delete_volume(volume1, True) - self.driver._delete_volume(volume3, True) - - # Snapshot volume that doesn't exist - snapshot = {} - snapshot["name"] = "snap_volume%s" % random.randint(10000, 99999) - snapshot["volume_name"] = "no_exist" - self.assertRaises(exception.VolumeNotFound, - self.driver.create_snapshot, snapshot) - - def test_storwize_svc_volumes(self): - # Create a first volume - volume = {} - volume["name"] = "test1_volume%s" % random.randint(10000, 99999) - volume["size"] = 10 - volume["id"] = 1 - - self.driver.create_volume(volume) - - self.driver.ensure_export(None, volume) - - # Do nothing - self.driver.create_export(None, volume) - self.driver.remove_export(None, volume) - self.assertRaises(NotImplementedError, - self.driver.check_for_export, None, volume["id"]) - - # Make sure volume attributes are as they should be - attributes = self.driver._get_volume_attributes(volume["name"]) - attr_size = float(attributes["capacity"]) / 1073741824 # bytes to GB - self.assertEqual(attr_size, float(volume["size"])) - pool = storwize_svc.FLAGS.storwize_svc_volpool_name - self.assertEqual(attributes["mdisk_grp_name"], pool) - - # Try to create the volume again (should fail) - self.assertRaises(exception.ProcessExecutionError, - self.driver.create_volume, volume) - - # Try to delete a volume that doesn't exist (should not fail) - vol_no_exist = {"name": "i_dont_exist"} - self.driver.delete_volume(vol_no_exist) - # Ensure export for volume that doesn't exist (should not fail) - self.driver.ensure_export(None, vol_no_exist) - - # Delete the volume - self.driver.delete_volume(volume) - - def _create_test_vol(self): - volume = {} - volume["name"] = "testparam_volume%s" % random.randint(10000, 99999) - volume["size"] = 1 - volume["id"] = 1 - self.driver.create_volume(volume) - - attrs = self.driver._get_volume_attributes(volume["name"]) - self.driver.delete_volume(volume) - return attrs - - def test_storwize_svc_volume_params(self): - # Option test matrix - # Option Value Covered by test # - # rsize -1 1 - # rsize 2% 2,3 - # warning 0 2 - # warning 80% 3 - # autoexpand True 2 - # autoexpand False 3 - # grainsize 32 2 - # grainsize 256 3 - # compression True 4 - # compression False 2,3 - # easytier True 1,3 - # easytier False 2 - - # Test 1 - self.flags(storwize_svc_vol_rsize="-1") - self.flags(storwize_svc_vol_easytier=True) - attrs = self._create_test_vol() - self.assertEquals(attrs["free_capacity"], "0") - self.assertEquals(attrs["easy_tier"], "on") - FLAGS.reset() - - # Test 2 - self.flags(storwize_svc_vol_rsize="2%") - self.flags(storwize_svc_vol_compression=False) - self.flags(storwize_svc_vol_warning="0") - self.flags(storwize_svc_vol_autoexpand=True) - self.flags(storwize_svc_vol_grainsize="32") - self.flags(storwize_svc_vol_easytier=False) - attrs = self._create_test_vol() - self.assertNotEqual(attrs["capacity"], attrs["real_capacity"]) - self.assertEquals(attrs["compressed_copy"], "no") - self.assertEquals(attrs["warning"], "0") - self.assertEquals(attrs["autoexpand"], "on") - self.assertEquals(attrs["grainsize"], "32") - self.assertEquals(attrs["easy_tier"], "off") - FLAGS.reset() - - # Test 3 - self.flags(storwize_svc_vol_rsize="2%") - self.flags(storwize_svc_vol_compression=False) - self.flags(storwize_svc_vol_warning="80%") - self.flags(storwize_svc_vol_autoexpand=False) - self.flags(storwize_svc_vol_grainsize="256") - self.flags(storwize_svc_vol_easytier=True) - attrs = self._create_test_vol() - self.assertNotEqual(attrs["capacity"], attrs["real_capacity"]) - self.assertEquals(attrs["compressed_copy"], "no") - self.assertEquals(attrs["warning"], "80") - self.assertEquals(attrs["autoexpand"], "off") - self.assertEquals(attrs["grainsize"], "256") - self.assertEquals(attrs["easy_tier"], "on") - FLAGS.reset() - - # Test 4 - self.flags(storwize_svc_vol_rsize="2%") - self.flags(storwize_svc_vol_compression=True) - try: - attrs = self._create_test_vol() - self.assertNotEqual(attrs["capacity"], attrs["real_capacity"]) - self.assertEquals(attrs["compressed_copy"], "yes") - except exception.ProcessExecutionError as e: - if "CMMVC7050E" not in e.stderr: - raise exception.ProcessExecutionError(exit_code=e.exit_code, - stdout=e.stdout, - stderr=e.stderr, - cmd=e.cmd) - if self.USESIM == 1: - self.sim.error_injection("mkvdisk", "no_compression") - self.assertRaises(exception.ProcessExecutionError, - self._create_test_vol) - FLAGS.reset() - - def test_storwize_svc_unicode_host_and_volume_names(self): - volume1 = {} - volume1["name"] = u"unicode1_volume%s" % random.randint(10000, 99999) - volume1["size"] = 2 - volume1["id"] = 1 - self.driver.create_volume(volume1) - # Make sure that the volumes have been created - is_volume_defined = self.driver._is_volume_defined(volume1["name"]) - self.assertEqual(is_volume_defined, True) - conn = {} - conn["initiator"] = u"unicode:init:%s" % random.randint(10000, 99999) - conn["ip"] = "10.10.10.10" # Bogus ip for testing - self.driver.initialize_connection(volume1, conn) - self.driver.terminate_connection(volume1, conn) - self.driver.delete_volume(volume1) - - def test_storwize_svc_host_maps(self): - # Create two volumes to be used in mappings - volume1 = {} - volume1["name"] = "test1_volume%s" % random.randint(10000, 99999) - volume1["size"] = 2 - volume1["id"] = 1 - self.driver.create_volume(volume1) - volume2 = {} - volume2["name"] = "test2_volume%s" % random.randint(10000, 99999) - volume2["size"] = 2 - volume2["id"] = 1 - self.driver.create_volume(volume2) - - # Check case where no hosts exist - if self.USESIM == 1: - ret = self.driver._get_host_from_iscsiname("foo") - self.assertEquals(ret, None) - ret = self.driver._is_host_defined("foo") - self.assertEquals(ret, False) - - # Make sure that the volumes have been created - is_volume_defined = self.driver._is_volume_defined(volume1["name"]) - self.assertEqual(is_volume_defined, True) - is_volume_defined = self.driver._is_volume_defined(volume2["name"]) - self.assertEqual(is_volume_defined, True) - - # Initialize connection from the first volume to a host - # Add some characters to the initiator name that should be converted - # when used for the host name - conn = {} - conn["initiator"] = "test:init:%s" % random.randint(10000, 99999) - conn["ip"] = "10.10.10.10" # Bogus ip for testing - self.driver.initialize_connection(volume1, conn) - - # Initialize again, should notice it and do nothing - self.driver.initialize_connection(volume1, conn) - - # Try to delete the 1st volume (should fail because it is mapped) - self.assertRaises(exception.ProcessExecutionError, - self.driver.delete_volume, volume1) - - # Test no preferred node - self.driver.terminate_connection(volume1, conn) - if self.USESIM == 1: - self.sim.error_injection("lsvdisk", "no_pref_node") - self.driver.initialize_connection(volume1, conn) - - # Initialize connection from the second volume to the host with no - # preferred node set if in simulation mode, otherwise, just - # another initialize connection. - if self.USESIM == 1: - self.sim.error_injection("lsvdisk", "blank_pref_node") - self.driver.initialize_connection(volume2, conn) - - # Try to remove connection from host that doesn't exist (should fail) - conn_no_exist = {"initiator": "i_dont_exist"} - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.terminate_connection, volume1, conn_no_exist) - - # Try to remove connection from volume that isn't mapped (should print - # message but NOT fail) - vol_no_exist = {"name": "i_dont_exist"} - self.driver.terminate_connection(vol_no_exist, conn) - - # Remove the mapping from the 1st volume and delete it - self.driver.terminate_connection(volume1, conn) - self.driver.delete_volume(volume1) - vol_def = self.driver._is_volume_defined(volume1["name"]) - self.assertEqual(vol_def, False) - - # Make sure our host still exists - host_name = self.driver._get_host_from_iscsiname(conn["initiator"]) - host_def = self.driver._is_host_defined(host_name) - self.assertEquals(host_def, True) - - # Remove the mapping from the 2nd volume and delete it. The host should - # be automatically removed because there are no more mappings. - self.driver.terminate_connection(volume2, conn) - self.driver.delete_volume(volume2) - vol_def = self.driver._is_volume_defined(volume2["name"]) - self.assertEqual(vol_def, False) - - # Check if our host still exists (it should not) - ret = self.driver._get_host_from_iscsiname(conn["initiator"]) - self.assertEquals(ret, None) - ret = self.driver._is_host_defined(host_name) - self.assertEquals(ret, False) diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py deleted file mode 100644 index 7b14d8a9..00000000 --- a/nova/tests/test_volume.py +++ /dev/null @@ -1,931 +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. -""" -Tests for Volume Code. - -""" - -import cStringIO -import datetime - -import mox -import os -import shutil -import tempfile - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova.openstack.common import importutils -from nova.openstack.common.notifier import api as notifier_api -from nova.openstack.common.notifier import test_notifier -from nova.openstack.common import rpc -import nova.policy -from nova import quota -from nova import test -from nova.tests.image import fake as fake_image -import nova.volume.api -from nova.volume import iscsi - -QUOTAS = quota.QUOTAS - - -FLAGS = flags.FLAGS - - -class VolumeTestCase(test.TestCase): - """Test Case for volumes.""" - - def setUp(self): - super(VolumeTestCase, self).setUp() - self.compute = importutils.import_object(FLAGS.compute_manager) - vol_tmpdir = tempfile.mkdtemp() - self.flags(compute_driver='nova.virt.fake.FakeDriver', - volumes_dir=vol_tmpdir, - notification_driver=[test_notifier.__name__]) - self.stubs.Set(iscsi.TgtAdm, '_get_target', self.fake_get_target) - self.volume = importutils.import_object(FLAGS.volume_manager) - self.context = context.get_admin_context() - instance = db.instance_create(self.context, {}) - self.instance_id = instance['id'] - self.instance_uuid = instance['uuid'] - test_notifier.NOTIFICATIONS = [] - fake_image.stub_out_image_service(self.stubs) - - def tearDown(self): - try: - shutil.rmtree(FLAGS.volumes_dir) - except OSError: - pass - db.instance_destroy(self.context, self.instance_uuid) - notifier_api._reset_drivers() - super(VolumeTestCase, self).tearDown() - - def fake_get_target(obj, iqn): - return 1 - - @staticmethod - def _create_volume(size=0, snapshot_id=None, image_id=None, metadata=None): - """Create a volume object.""" - vol = {} - vol['size'] = size - vol['snapshot_id'] = snapshot_id - vol['image_id'] = image_id - vol['user_id'] = 'fake' - vol['project_id'] = 'fake' - vol['availability_zone'] = FLAGS.storage_availability_zone - vol['status'] = "creating" - vol['attach_status'] = "detached" - if metadata is not None: - vol['metadata'] = metadata - return db.volume_create(context.get_admin_context(), vol) - - def test_ec2_uuid_mapping(self): - ec2_vol = db.ec2_volume_create(context.get_admin_context(), - 'aaaaaaaa-bbbb-bbbb-bbbb-aaaaaaaaaaaa', 5) - self.assertEqual(5, ec2_vol['id']) - self.assertEqual('aaaaaaaa-bbbb-bbbb-bbbb-aaaaaaaaaaaa', - db.get_volume_uuid_by_ec2_id(context.get_admin_context(), 5)) - - ec2_vol = db.ec2_volume_create(context.get_admin_context(), - 'aaaaaaaa-bbbb-bbbb-bbbb-aaaaaaaaaaaa', 1) - self.assertEqual(1, ec2_vol['id']) - - ec2_vol = db.ec2_volume_create(context.get_admin_context(), - 'aaaaaaaa-bbbb-bbbb-bbbb-aaaaaaaaazzz') - self.assertEqual(6, ec2_vol['id']) - - def test_create_delete_volume(self): - """Test volume can be created and deleted.""" - # Need to stub out reserve, commit, and rollback - def fake_reserve(context, expire=None, **deltas): - return ["RESERVATION"] - - def fake_commit(context, reservations): - pass - - def fake_rollback(context, reservations): - pass - - self.stubs.Set(QUOTAS, "reserve", fake_reserve) - self.stubs.Set(QUOTAS, "commit", fake_commit) - self.stubs.Set(QUOTAS, "rollback", fake_rollback) - - volume = self._create_volume() - volume_id = volume['id'] - self.assertEquals(len(test_notifier.NOTIFICATIONS), 0) - self.volume.create_volume(self.context, volume_id) - self.assertEquals(len(test_notifier.NOTIFICATIONS), 2) - self.assertEqual(volume_id, db.volume_get(context.get_admin_context(), - volume_id).id) - - self.volume.delete_volume(self.context, volume_id) - self.assertEquals(len(test_notifier.NOTIFICATIONS), 4) - self.assertRaises(exception.NotFound, - db.volume_get, - self.context, - volume_id) - - def test_create_delete_volume_with_metadata(self): - """Test volume can be created and deleted.""" - test_meta = {'fake_key': 'fake_value'} - volume = self._create_volume('0', None, metadata=test_meta) - volume_id = volume['id'] - self.volume.create_volume(self.context, volume_id) - result_meta = { - volume.volume_metadata[0].key: volume.volume_metadata[0].value} - self.assertEqual(result_meta, test_meta) - - self.volume.delete_volume(self.context, volume_id) - self.assertRaises(exception.NotFound, - db.volume_get, - self.context, - volume_id) - - def _do_test_create_over_quota(self, resource, expected): - """Test volume creation over quota.""" - - def fake_reserve(context, **deltas): - kwargs = dict(overs=[resource], - quotas=dict(gigabytes=1000, volumes=10), - usages=dict(gigabytes=dict(reserved=1, in_use=999), - volumes=dict(reserved=1, in_use=9))) - raise exception.OverQuota(**kwargs) - - def fake_commit(context, reservations): - self.fail('should not commit over quota') - - self.stubs.Set(QUOTAS, 'reserve', fake_reserve) - self.stubs.Set(QUOTAS, 'commit', fake_commit) - - volume_api = nova.volume.api.API() - - self.assertRaises(expected, - volume_api.create, - self.context, - 2, - 'name', - 'description') - - def test_create_volumes_over_quota(self): - self._do_test_create_over_quota('volumes', - exception.VolumeLimitExceeded) - - def test_create_gigabytes_over_quota(self): - self._do_test_create_over_quota('gigabytes', - exception.VolumeSizeTooLarge) - - def test_delete_busy_volume(self): - """Test volume survives deletion if driver reports it as busy.""" - volume = self._create_volume() - volume_id = volume['id'] - self.volume.create_volume(self.context, volume_id) - - self.mox.StubOutWithMock(self.volume.driver, 'delete_volume') - self.volume.driver.delete_volume(mox.IgnoreArg()).AndRaise( - exception.VolumeIsBusy) - self.mox.ReplayAll() - res = self.volume.delete_volume(self.context, volume_id) - self.assertEqual(True, res) - volume_ref = db.volume_get(context.get_admin_context(), volume_id) - self.assertEqual(volume_id, volume_ref.id) - self.assertEqual("available", volume_ref.status) - - self.mox.UnsetStubs() - self.volume.delete_volume(self.context, volume_id) - - def test_create_volume_from_snapshot(self): - """Test volume can be created from a snapshot.""" - volume_src = self._create_volume() - self.volume.create_volume(self.context, volume_src['id']) - snapshot_id = self._create_snapshot(volume_src['id']) - self.volume.create_snapshot(self.context, volume_src['id'], - snapshot_id) - volume_dst = self._create_volume(0, snapshot_id) - self.volume.create_volume(self.context, volume_dst['id'], snapshot_id) - self.assertEqual(volume_dst['id'], - db.volume_get( - context.get_admin_context(), - volume_dst['id']).id) - self.assertEqual(snapshot_id, db.volume_get( - context.get_admin_context(), - volume_dst['id']).snapshot_id) - - self.volume.delete_volume(self.context, volume_dst['id']) - self.volume.delete_snapshot(self.context, snapshot_id) - self.volume.delete_volume(self.context, volume_src['id']) - - def test_too_big_volume(self): - """Ensure failure if a too large of a volume is requested.""" - # FIXME(vish): validation needs to move into the data layer in - # volume_create - return True - try: - volume = self._create_volume('1001') - self.volume.create_volume(self.context, volume) - self.fail("Should have thrown TypeError") - except TypeError: - pass - - def test_run_attach_detach_volume(self): - """Make sure volume can be attached and detached from instance.""" - inst = {} - inst['image_id'] = 1 - inst['reservation_id'] = 'r-fakeres' - inst['launch_time'] = '10' - inst['user_id'] = 'fake' - inst['project_id'] = 'fake' - inst['instance_type_id'] = '2' # m1.tiny - inst['ami_launch_index'] = 0 - instance = db.instance_create(self.context, {}) - instance_id = instance['id'] - instance_uuid = instance['uuid'] - mountpoint = "/dev/sdf" - volume = self._create_volume() - volume_id = volume['id'] - self.volume.create_volume(self.context, volume_id) - if FLAGS.fake_tests: - db.volume_attached(self.context, volume_id, instance_uuid, - mountpoint) - else: - self.compute.attach_volume(self.context, - instance_uuid, - volume_id, - mountpoint) - vol = db.volume_get(context.get_admin_context(), volume_id) - self.assertEqual(vol['status'], "in-use") - self.assertEqual(vol['attach_status'], "attached") - self.assertEqual(vol['mountpoint'], mountpoint) - self.assertEqual(vol['instance_uuid'], instance_uuid) - self.assertNotEqual(vol['attach_time'], None) - - self.assertRaises(exception.VolumeAttached, - self.volume.delete_volume, - self.context, - volume_id) - if FLAGS.fake_tests: - db.volume_detached(self.context, volume_id) - else: - self.compute.detach_volume(self.context, - instance_uuid, - volume_id) - vol = db.volume_get(self.context, volume_id) - self.assertEqual(vol['status'], "available") - self.assertEqual(vol['attach_time'], None) - - self.volume.delete_volume(self.context, volume_id) - self.assertRaises(exception.VolumeNotFound, - db.volume_get, - self.context, - volume_id) - db.instance_destroy(self.context, instance_uuid) - - def test_concurrent_volumes_get_different_targets(self): - """Ensure multiple concurrent volumes get different targets.""" - volume_ids = [] - targets = [] - - def _check(volume_id): - """Make sure targets aren't duplicated.""" - volume_ids.append(volume_id) - admin_context = context.get_admin_context() - iscsi_target = db.volume_get_iscsi_target_num(admin_context, - volume_id) - self.assert_(iscsi_target not in targets) - targets.append(iscsi_target) - - total_slots = FLAGS.iscsi_num_targets - for _index in xrange(total_slots): - self._create_volume() - for volume_id in volume_ids: - self.volume.delete_volume(self.context, volume_id) - - def test_multi_node(self): - # TODO(termie): Figure out how to test with two nodes, - # each of them having a different FLAG for storage_node - # This will allow us to test cross-node interactions - pass - - @staticmethod - def _create_snapshot(volume_id, size='0'): - """Create a snapshot object.""" - snap = {} - snap['volume_size'] = size - snap['user_id'] = 'fake' - snap['project_id'] = 'fake' - snap['volume_id'] = volume_id - snap['status'] = "creating" - return db.snapshot_create(context.get_admin_context(), snap)['id'] - - def test_create_delete_snapshot(self): - """Test snapshot can be created and deleted.""" - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - snapshot_id = self._create_snapshot(volume['id']) - self.volume.create_snapshot(self.context, volume['id'], snapshot_id) - self.assertEqual(snapshot_id, - db.snapshot_get(context.get_admin_context(), - snapshot_id).id) - - self.volume.delete_snapshot(self.context, snapshot_id) - self.assertRaises(exception.NotFound, - db.snapshot_get, - self.context, - snapshot_id) - self.volume.delete_volume(self.context, volume['id']) - - def test_cant_delete_volume_in_use(self): - """Test volume can't be deleted in invalid stats.""" - # create a volume and assign to host - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - volume['status'] = 'in-use' - volume['host'] = 'fakehost' - - volume_api = nova.volume.api.API() - - # 'in-use' status raises InvalidVolume - self.assertRaises(exception.InvalidVolume, - volume_api.delete, - self.context, - volume) - - # clean up - self.volume.delete_volume(self.context, volume['id']) - - def test_force_delete_volume(self): - """Test volume can be forced to delete.""" - # create a volume and assign to host - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - volume['status'] = 'error_deleting' - volume['host'] = 'fakehost' - - volume_api = nova.volume.api.API() - - # 'error_deleting' volumes can't be deleted - self.assertRaises(exception.InvalidVolume, - volume_api.delete, - self.context, - volume) - - # delete with force - volume_api.delete(self.context, volume, force=True) - - # status is deleting - volume = db.volume_get(context.get_admin_context(), volume['id']) - self.assertEquals(volume['status'], 'deleting') - - # clean up - self.volume.delete_volume(self.context, volume['id']) - - def test_cant_delete_volume_with_snapshots(self): - """Test snapshot can be created and deleted.""" - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - snapshot_id = self._create_snapshot(volume['id']) - self.volume.create_snapshot(self.context, volume['id'], snapshot_id) - self.assertEqual(snapshot_id, - db.snapshot_get(context.get_admin_context(), - snapshot_id).id) - - volume['status'] = 'available' - volume['host'] = 'fakehost' - - volume_api = nova.volume.api.API() - - self.assertRaises(exception.InvalidVolume, - volume_api.delete, - self.context, - volume) - self.volume.delete_snapshot(self.context, snapshot_id) - self.volume.delete_volume(self.context, volume['id']) - - def test_can_delete_errored_snapshot(self): - """Test snapshot can be created and deleted.""" - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - snapshot_id = self._create_snapshot(volume['id']) - self.volume.create_snapshot(self.context, volume['id'], snapshot_id) - snapshot = db.snapshot_get(context.get_admin_context(), - snapshot_id) - - volume_api = nova.volume.api.API() - - snapshot['status'] = 'badstatus' - self.assertRaises(exception.InvalidVolume, - volume_api.delete_snapshot, - self.context, - snapshot) - - snapshot['status'] = 'error' - self.volume.delete_snapshot(self.context, snapshot_id) - self.volume.delete_volume(self.context, volume['id']) - - def test_create_snapshot_force(self): - """Test snapshot in use can be created forcibly.""" - - def fake_cast(ctxt, topic, msg): - pass - self.stubs.Set(rpc, 'cast', fake_cast) - - volume = self._create_volume() - self.volume.create_volume(self.context, volume['id']) - db.volume_attached(self.context, volume['id'], self.instance_uuid, - '/dev/sda1') - - volume_api = nova.volume.api.API() - volume = volume_api.get(self.context, volume['id']) - self.assertRaises(exception.InvalidVolume, - volume_api.create_snapshot, - self.context, volume, - 'fake_name', 'fake_description') - snapshot_ref = volume_api.create_snapshot_force(self.context, - volume, - 'fake_name', - 'fake_description') - db.snapshot_destroy(self.context, snapshot_ref['id']) - db.volume_destroy(self.context, volume['id']) - - def test_delete_busy_snapshot(self): - """Test snapshot can be created and deleted.""" - volume = self._create_volume() - volume_id = volume['id'] - self.volume.create_volume(self.context, volume_id) - snapshot_id = self._create_snapshot(volume_id) - self.volume.create_snapshot(self.context, volume_id, snapshot_id) - - self.mox.StubOutWithMock(self.volume.driver, 'delete_snapshot') - self.volume.driver.delete_snapshot(mox.IgnoreArg()).AndRaise( - exception.SnapshotIsBusy) - self.mox.ReplayAll() - self.volume.delete_snapshot(self.context, snapshot_id) - snapshot_ref = db.snapshot_get(self.context, snapshot_id) - self.assertEqual(snapshot_id, snapshot_ref.id) - self.assertEqual("available", snapshot_ref.status) - - self.mox.UnsetStubs() - self.volume.delete_snapshot(self.context, snapshot_id) - self.volume.delete_volume(self.context, volume_id) - - def test_create_volume_usage_notification(self): - """Ensure create volume generates appropriate usage notification""" - volume = self._create_volume() - volume_id = volume['id'] - self.assertEquals(len(test_notifier.NOTIFICATIONS), 0) - self.volume.create_volume(self.context, volume_id) - self.assertEquals(len(test_notifier.NOTIFICATIONS), 2) - msg = test_notifier.NOTIFICATIONS[0] - self.assertEquals(msg['event_type'], 'volume.create.start') - payload = msg['payload'] - self.assertEquals(payload['status'], 'creating') - msg = test_notifier.NOTIFICATIONS[1] - self.assertEquals(msg['priority'], 'INFO') - self.assertEquals(msg['event_type'], 'volume.create.end') - payload = msg['payload'] - self.assertEquals(payload['tenant_id'], volume['project_id']) - self.assertEquals(payload['user_id'], volume['user_id']) - self.assertEquals(payload['volume_id'], volume['id']) - self.assertEquals(payload['status'], 'available') - self.assertEquals(payload['size'], volume['size']) - self.assertTrue('display_name' in payload) - self.assertTrue('snapshot_id' in payload) - self.assertTrue('launched_at' in payload) - self.assertTrue('created_at' in payload) - self.volume.delete_volume(self.context, volume_id) - - def _do_test_create_volume_with_size(self, size): - def fake_reserve(context, expire=None, **deltas): - return ["RESERVATION"] - - def fake_commit(context, reservations): - pass - - def fake_rollback(context, reservations): - pass - - self.stubs.Set(QUOTAS, "reserve", fake_reserve) - self.stubs.Set(QUOTAS, "commit", fake_commit) - self.stubs.Set(QUOTAS, "rollback", fake_rollback) - - volume_api = nova.volume.api.API() - - volume = volume_api.create(self.context, - size, - 'name', - 'description') - self.assertEquals(volume['size'], int(size)) - - def test_create_volume_int_size(self): - """Test volume creation with int size.""" - self._do_test_create_volume_with_size(2) - - def test_create_volume_string_size(self): - """Test volume creation with string size.""" - self._do_test_create_volume_with_size('2') - - def test_create_volume_with_bad_size(self): - def fake_reserve(context, expire=None, **deltas): - return ["RESERVATION"] - - def fake_commit(context, reservations): - pass - - def fake_rollback(context, reservations): - pass - - self.stubs.Set(QUOTAS, "reserve", fake_reserve) - self.stubs.Set(QUOTAS, "commit", fake_commit) - self.stubs.Set(QUOTAS, "rollback", fake_rollback) - - volume_api = nova.volume.api.API() - - self.assertRaises(exception.InvalidInput, - volume_api.create, - self.context, - '2Gb', - 'name', - 'description') - - def test_begin_roll_detaching_volume(self): - """Test begin_detaching and roll_detaching functions.""" - volume = self._create_volume() - volume_api = nova.volume.api.API() - volume_api.begin_detaching(self.context, volume) - volume = db.volume_get(self.context, volume['id']) - self.assertEqual(volume['status'], "detaching") - volume_api.roll_detaching(self.context, volume) - volume = db.volume_get(self.context, volume['id']) - self.assertEqual(volume['status'], "in-use") - - def _create_volume_from_image(self, expected_status, - fakeout_copy_image_to_volume=False): - """Call copy image to volume, Test the status of volume after calling - copying image to volume.""" - def fake_local_path(volume): - return dst_path - - def fake_copy_image_to_volume(context, volume, image_id): - pass - - dst_fd, dst_path = tempfile.mkstemp() - os.close(dst_fd) - self.stubs.Set(self.volume.driver, 'local_path', fake_local_path) - if fakeout_copy_image_to_volume: - self.stubs.Set(self.volume, '_copy_image_to_volume', - fake_copy_image_to_volume) - - image_id = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77' - volume_id = 1 - # creating volume testdata - db.volume_create(self.context, {'id': volume_id, - 'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1), - 'display_description': 'Test Desc', - 'size': 20, - 'status': 'creating', - 'instance_uuid': None, - 'host': 'dummy'}) - try: - 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) - os.unlink(dst_path) - - def test_create_volume_from_image_status_downloading(self): - """Verify that before copying image to volume, it is in downloading - state.""" - self._create_volume_from_image('downloading', True) - - def test_create_volume_from_image_status_available(self): - """Verify that before copying image to volume, it is in available - state.""" - self._create_volume_from_image('available') - - def test_create_volume_from_image_exception(self): - """Verify that create volume from image, the volume status is - 'downloading'.""" - dst_fd, dst_path = tempfile.mkstemp() - os.close(dst_fd) - - self.stubs.Set(self.volume.driver, 'local_path', lambda x: dst_path) - - image_id = 'aaaaaaaa-0000-0000-0000-000000000000' - # creating volume testdata - volume_id = 1 - db.volume_create(self.context, {'id': volume_id, - 'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1), - 'display_description': 'Test Desc', - 'size': 20, - 'status': 'creating', - 'host': 'dummy'}) - - self.assertRaises(exception.ImageNotFound, - self.volume.create_volume, - self.context, - volume_id, - None, - image_id) - volume = db.volume_get(self.context, volume_id) - self.assertEqual(volume['status'], "error") - # cleanup - db.volume_destroy(self.context, volume_id) - os.unlink(dst_path) - - def test_copy_volume_to_image_status_available(self): - dst_fd, dst_path = tempfile.mkstemp() - os.close(dst_fd) - - def fake_local_path(volume): - return dst_path - - self.stubs.Set(self.volume.driver, 'local_path', fake_local_path) - - image_id = '70a599e0-31e7-49b7-b260-868f441e862b' - # creating volume testdata - volume_id = 1 - db.volume_create(self.context, {'id': volume_id, - 'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1), - 'display_description': 'Test Desc', - 'size': 20, - 'status': 'uploading', - 'instance_uuid': None, - 'host': 'dummy'}) - - try: - # start test - self.volume.copy_volume_to_image(self.context, - volume_id, - image_id) - - volume = db.volume_get(self.context, volume_id) - self.assertEqual(volume['status'], 'available') - finally: - # cleanup - db.volume_destroy(self.context, volume_id) - os.unlink(dst_path) - - def test_copy_volume_to_image_status_use(self): - dst_fd, dst_path = tempfile.mkstemp() - os.close(dst_fd) - - def fake_local_path(volume): - return dst_path - - self.stubs.Set(self.volume.driver, 'local_path', fake_local_path) - - #image_id = '70a599e0-31e7-49b7-b260-868f441e862b' - image_id = 'a440c04b-79fa-479c-bed1-0b816eaec379' - # creating volume testdata - volume_id = 1 - db.volume_create(self.context, - {'id': volume_id, - 'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1), - 'display_description': 'Test Desc', - 'size': 20, - 'status': 'uploading', - 'instance_uuid': - 'b21f957d-a72f-4b93-b5a5-45b1161abb02', - 'host': 'dummy'}) - - try: - # start test - self.volume.copy_volume_to_image(self.context, - volume_id, - image_id) - - volume = db.volume_get(self.context, volume_id) - self.assertEqual(volume['status'], 'in-use') - finally: - # cleanup - db.volume_destroy(self.context, volume_id) - os.unlink(dst_path) - - def test_copy_volume_to_image_exception(self): - dst_fd, dst_path = tempfile.mkstemp() - os.close(dst_fd) - - def fake_local_path(volume): - return dst_path - - self.stubs.Set(self.volume.driver, 'local_path', fake_local_path) - - image_id = 'aaaaaaaa-0000-0000-0000-000000000000' - # creating volume testdata - volume_id = 1 - db.volume_create(self.context, {'id': volume_id, - 'updated_at': datetime.datetime(1, 1, 1, 1, 1, 1), - 'display_description': 'Test Desc', - 'size': 20, - 'status': 'in-use', - 'host': 'dummy'}) - - try: - # start test - self.assertRaises(exception.ImageNotFound, - self.volume.copy_volume_to_image, - self.context, - volume_id, - image_id) - - volume = db.volume_get(self.context, volume_id) - self.assertEqual(volume['status'], 'available') - finally: - # cleanup - db.volume_destroy(self.context, volume_id) - os.unlink(dst_path) - - def test_create_volume_from_exact_sized_image(self): - """Verify that an image which is exactly the same size as the - volume, will work correctly.""" - class _FakeImageService: - def __init__(self, db_driver=None, image_service=None): - pass - - def show(self, context, image_id): - return {'size': 2 * 1024 * 1024 * 1024} - - image_id = '70a599e0-31e7-49b7-b260-868f441e862b' - - try: - volume_id = None - volume_api = nova.volume.api.API( - image_service=_FakeImageService()) - volume = volume_api.create(self.context, 2, 'name', 'description', - image_id=1) - volume_id = volume['id'] - self.assertEqual(volume['status'], 'creating') - - finally: - # cleanup - db.volume_destroy(self.context, volume_id) - - def test_create_volume_from_oversized_image(self): - """Verify that an image which is too big will fail correctly.""" - class _FakeImageService: - def __init__(self, db_driver=None, image_service=None): - pass - - def show(self, context, image_id): - return {'size': 2 * 1024 * 1024 * 1024 + 1} - - image_id = '70a599e0-31e7-49b7-b260-868f441e862b' - - volume_api = nova.volume.api.API(image_service=_FakeImageService()) - - self.assertRaises(exception.InvalidInput, - volume_api.create, - self.context, 2, - 'name', 'description', image_id=1) - - -class DriverTestCase(test.TestCase): - """Base Test class for Drivers.""" - driver_name = "nova.volume.driver.FakeBaseDriver" - - def setUp(self): - super(DriverTestCase, self).setUp() - vol_tmpdir = tempfile.mkdtemp() - self.flags(volume_driver=self.driver_name, - volumes_dir=vol_tmpdir) - self.volume = importutils.import_object(FLAGS.volume_manager) - self.context = context.get_admin_context() - self.output = "" - self.stubs.Set(iscsi.TgtAdm, '_get_target', self.fake_get_target) - - def _fake_execute(_command, *_args, **_kwargs): - """Fake _execute.""" - return self.output, None - self.volume.driver.set_execute(_fake_execute) - - instance = db.instance_create(self.context, {}) - self.instance_id = instance['id'] - self.instance_uuid = instance['uuid'] - - def tearDown(self): - try: - shutil.rmtree(FLAGS.volumes_dir) - except OSError: - pass - super(DriverTestCase, self).tearDown() - - def fake_get_target(obj, iqn): - return 1 - - def _attach_volume(self): - """Attach volumes to an instance.""" - return [] - - def _detach_volume(self, volume_id_list): - """Detach volumes from an instance.""" - for volume_id in volume_id_list: - db.volume_detached(self.context, volume_id) - self.volume.delete_volume(self.context, volume_id) - - -class VolumeDriverTestCase(DriverTestCase): - """Test case for VolumeDriver""" - driver_name = "nova.volume.driver.VolumeDriver" - - def test_delete_busy_volume(self): - """Test deleting a busy volume.""" - self.stubs.Set(self.volume.driver, '_volume_not_present', - lambda x: False) - self.stubs.Set(self.volume.driver, '_delete_volume', - lambda x, y: False) - # Want DriverTestCase._fake_execute to return 'o' so that - # volume.driver.delete_volume() raises the VolumeIsBusy exception. - self.output = 'o' - self.assertRaises(exception.VolumeIsBusy, - self.volume.driver.delete_volume, - {'name': 'test1', 'size': 1024}) - # when DriverTestCase._fake_execute returns something other than - # 'o' volume.driver.delete_volume() does not raise an exception. - self.output = 'x' - self.volume.driver.delete_volume({'name': 'test1', 'size': 1024}) - - -class ISCSITestCase(DriverTestCase): - """Test Case for ISCSIDriver""" - driver_name = "nova.volume.driver.ISCSIDriver" - - def _attach_volume(self): - """Attach volumes to an instance. """ - volume_id_list = [] - for index in xrange(3): - vol = {} - vol['size'] = 0 - vol_ref = db.volume_create(self.context, vol) - self.volume.create_volume(self.context, vol_ref['id']) - vol_ref = db.volume_get(self.context, vol_ref['id']) - - # each volume has a different mountpoint - mountpoint = "/dev/sd" + chr((ord('b') + index)) - db.volume_attached(self.context, vol_ref['id'], self.instance_uuid, - mountpoint) - volume_id_list.append(vol_ref['id']) - - return volume_id_list - - def test_check_for_export_with_no_volume(self): - self.volume.check_for_export(self.context, self.instance_id) - - -class VolumePolicyTestCase(test.TestCase): - - def setUp(self): - super(VolumePolicyTestCase, self).setUp() - - nova.policy.reset() - nova.policy.init() - - self.context = context.get_admin_context() - - def tearDown(self): - super(VolumePolicyTestCase, self).tearDown() - nova.policy.reset() - - def _set_rules(self, rules): - nova.common.policy.set_brain(nova.common.policy.HttpBrain(rules)) - - def test_check_policy(self): - self.mox.StubOutWithMock(nova.policy, 'enforce') - target = { - 'project_id': self.context.project_id, - 'user_id': self.context.user_id, - } - nova.policy.enforce(self.context, 'volume:attach', target) - self.mox.ReplayAll() - nova.volume.api.check_policy(self.context, 'attach') - - def test_check_policy_with_target(self): - self.mox.StubOutWithMock(nova.policy, 'enforce') - target = { - 'project_id': self.context.project_id, - 'user_id': self.context.user_id, - 'id': 2, - } - nova.policy.enforce(self.context, 'volume:attach', target) - self.mox.ReplayAll() - nova.volume.api.check_policy(self.context, 'attach', {'id': 2}) diff --git a/nova/tests/test_volume_types.py b/nova/tests/test_volume_types.py deleted file mode 100644 index 5e178d5e..00000000 --- a/nova/tests/test_volume_types.py +++ /dev/null @@ -1,167 +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 nova import context -from nova.db.sqlalchemy import models -from nova.db.sqlalchemy import session as sql_session -from nova import exception -from nova import flags -from nova.openstack.common import log as logging -from nova import test -from nova.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) - - 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 does not 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, self.vol_type1_name) - 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_non_existent_vol_type_shouldnt_delete(self): - """Ensures that volume type creation fails with invalid args""" - self.assertRaises(exception.VolumeTypeNotFoundByName, - 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" - volume_types.create(self.ctxt, new_name) - volume_types.destroy(self.ctxt, new_name) - 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"}) diff --git a/nova/tests/test_volume_types_extra_specs.py b/nova/tests/test_volume_types_extra_specs.py deleted file mode 100644 index ed7840e0..00000000 --- a/nova/tests/test_volume_types_extra_specs.py +++ /dev/null @@ -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 nova import context -from nova import db -from nova 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['name']) - db.volume_type_destroy(context.get_admin_context(), - self.vol_type2_noextra['name']) - 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'], {}) diff --git a/nova/tests/test_volume_utils.py b/nova/tests/test_volume_utils.py deleted file mode 100644 index 89ad7c3d..00000000 --- a/nova/tests/test_volume_utils.py +++ /dev/null @@ -1,91 +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 nova import context -from nova import db -from nova import flags -from nova.openstack.common import importutils -from nova.openstack.common import log as logging -from nova.openstack.common.notifier import api as notifier_api -from nova.openstack.common.notifier import test_notifier -from nova import test -from nova.tests import fake_network -from nova.volume import utils as volume_utils - - -LOG = logging.getLogger(__name__) -FLAGS = flags.FLAGS - - -class UsageInfoTestCase(test.TestCase): - - def setUp(self): - super(UsageInfoTestCase, self).setUp() - self.flags(compute_driver='nova.virt.fake.FakeDriver', - host='fake', - notification_driver=[test_notifier.__name__]) - fake_network.set_stub_network_methods(self.stubs) - - 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) - 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']) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index a7bfa754..11e8844c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -170,7 +170,7 @@ class XenAPIVolumeTestCase(stubs.XenAPITestBase): vol['user_id'] = 'fake' vol['project_id'] = 'fake' vol['host'] = 'localhost' - vol['availability_zone'] = FLAGS.storage_availability_zone + vol['availability_zone'] = FLAGS.node_availability_zone vol['status'] = "creating" vol['attach_status'] = "detached" return db.volume_create(self.context, vol) diff --git a/nova/tests/test_xensm.py b/nova/tests/test_xensm.py deleted file mode 100644 index 2f2108aa..00000000 --- a/nova/tests/test_xensm.py +++ /dev/null @@ -1,140 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2010 Citrix Systems, 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. - -"""Test suite for Xen Storage Manager Volume Driver.""" - - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova.openstack.common import log as logging -from nova.tests.xenapi import stubs -from nova.virt.xenapi import fake as xenapi_fake -from nova.volume import xensm - -LOG = logging.getLogger(__name__) - -FLAGS = flags.FLAGS - - -class XenSMTestCase(stubs.XenAPITestBase): - """Unit tests for Xen Storage Manager Volume operations.""" - - def _get_sm_backend_params(self): - config_params = ("name_label=testsmbackend " - "server=localhost " - "serverpath=/tmp/nfspath") - params = dict(flavor_id=1, - sr_uuid=None, - sr_type='nfs', - config_params=config_params) - return params - - def setUp(self): - super(XenSMTestCase, self).setUp() - self.user_id = 'fake' - self.project_id = 'fake' - self.context = context.RequestContext(self.user_id, self.project_id) - self.flags(compute_driver='xenapi.XenAPIDriver', - xenapi_connection_url='http://test_url', - xenapi_connection_username='test_user', - xenapi_connection_password='test_pass') - stubs.stubout_session(self.stubs, xenapi_fake.SessionBase) - xenapi_fake.reset() - self.driver = xensm.XenSMDriver() - self.driver.db = db - - def _setup_step(self, ctxt): - # Create a fake backend conf - params = self._get_sm_backend_params() - beconf = db.sm_backend_conf_create(ctxt, - params) - # Call setup, the one time operation that will create a backend SR - self.driver.do_setup(ctxt) - return beconf - - def test_do_setup(self): - ctxt = context.get_admin_context() - beconf = self._setup_step(ctxt) - beconf = db.sm_backend_conf_get(ctxt, beconf['id']) - self.assertIsInstance(beconf['sr_uuid'], basestring) - - def _create_volume(self, size=0): - """Create a volume object.""" - vol = {} - vol['size'] = size - vol['user_id'] = 'fake' - vol['project_id'] = 'fake' - vol['host'] = 'localhost' - vol['availability_zone'] = FLAGS.storage_availability_zone - vol['status'] = "creating" - vol['attach_status'] = "detached" - return db.volume_create(self.context, vol) - - def test_create_volume(self): - ctxt = context.get_admin_context() - beconf = self._setup_step(ctxt) - volume = self._create_volume() - self.driver.create_volume(volume) - db.sm_volume_get(ctxt, volume['id']) - - def test_local_path(self): - ctxt = context.get_admin_context() - volume = self._create_volume() - val = self.driver.local_path(volume) - self.assertIsInstance(val, basestring) - - def test_delete_volume(self): - ctxt = context.get_admin_context() - beconf = self._setup_step(ctxt) - volume = self._create_volume() - self.driver.create_volume(volume) - self.driver.delete_volume(volume) - self.assertRaises(exception.NotFound, - db.sm_volume_get, - ctxt, - volume['id']) - - def test_delete_volume_raises_notfound(self): - ctxt = context.get_admin_context() - beconf = self._setup_step(ctxt) - self.assertRaises(exception.NotFound, - self.driver.delete_volume, - {'id': "FA15E-1D"}) - - def _get_expected_conn(self, beconf, vol): - expected = {} - expected['volume_id'] = unicode(vol['id']) - expected['flavor_id'] = beconf['flavor_id'] - expected['sr_uuid'] = unicode(beconf['sr_uuid']) - expected['sr_type'] = unicode(beconf['sr_type']) - return expected - - def test_initialize_connection(self): - ctxt = context.get_admin_context() - beconf = self._setup_step(ctxt) - beconf = db.sm_backend_conf_get(ctxt, beconf['id']) - volume = self._create_volume() - self.driver.create_volume(volume) - expected = self._get_expected_conn(beconf, volume) - conn = self.driver.initialize_connection(volume, 'fakeConnector') - res = {} - for key in ['volume_id', 'flavor_id', 'sr_uuid', 'sr_type']: - res[key] = conn['data'][key] - self.assertDictMatch(expected, res) - self.assertEqual(set(conn['data']['introduce_sr_keys']), - set([u'sr_type', u'server', u'serverpath']))