removes the nova-volume code from nova

This removes the majority of the nova-volume code from the codebase.
It updates relevent config options to default to cinder. It updates a
number of existing tests that were depending on code that was removed.

A few things still need to be removed:

 * volume/driver.py & volume/iscsi.py
   These files are used by the libvirt volume driver tests. These
   tests should be updated to mock the relevant calls.

 * scheduler/simple.py & scheduler/multi.py
   These files should no longer be necessary so they can be removed
   in a subsequent patch

 * exception.py cleanup
   Once the above files are removed there are a number of unused
   exceptions which can be removed

 * database calls and database tables
   The database calls have not been removed and the tables have not
   been dropped. This can be done in a separate migration

 * additional config options and nova.conf.sample
   There may be a few extra config options that can be removed and the
   conf sample can be regenerated

Implements bp delete-nova-volume

Change-Id: I0b540e54dbabd26901a7530035a38583bb521fda
This commit is contained in:
Vishvananda Ishaya 2012-10-23 19:30:48 -07:00
parent 17f4641ca1
commit eac2536308
39 changed files with 16 additions and 6451 deletions

View File

@ -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):

View File

@ -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()

View File

@ -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='<volume id>',
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),
]

View File

@ -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()

View File

@ -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")

View File

@ -328,14 +328,6 @@
"namespace": "http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1",
"updated": "2011-08-17T00:00:00+00:00"
},
{
"alias": "os-volume-types",
"description": "Volume types support",
"links": [],
"name": "VolumeTypes",
"namespace": "http://docs.openstack.org/compute/ext/volume_types/api/v1.1",
"updated": "2011-08-24T00:00:00+00:00"
},
{
"alias": "os-volumes",
"description": "Volumes support",

View File

@ -137,9 +137,6 @@
<extension alias="os-virtual-interfaces" updated="2011-08-17T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1" name="VirtualInterfaces">
<description>Virtual interface support</description>
</extension>
<extension alias="os-volume-types" updated="2011-08-24T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/volume_types/api/v1.1" name="VolumeTypes">
<description>Volume types support</description>
</extension>
<extension alias="os-volumes" updated="2011-03-25T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/volumes/api/v1.1" name="Volumes">
<description>Volumes support</description>
</extension>

View File

@ -127,8 +127,6 @@ man_pages = [
[u'OpenStack'], 1),
('man/nova-api-os-compute', 'nova-api-os-compute',
u'Cloud controller fabric', [u'OpenStack'], 1),
('man/nova-api-os-volume', 'nova-api-os-volume',
u'Cloud controller fabric', [u'OpenStack'], 1),
('man/nova-api', 'nova-api', u'Cloud controller fabric',
[u'OpenStack'], 1),
('man/nova-cert', 'nova-cert', u'Cloud controller fabric',
@ -155,10 +153,6 @@ man_pages = [
[u'OpenStack'], 1),
('man/nova-scheduler', 'nova-scheduler', u'Cloud controller fabric',
[u'OpenStack'], 1),
('man/nova-volume-usage-audit', 'nova-volume-usage-audit', u'Cloud controller fabric',
[u'OpenStack'], 1),
('man/nova-volume', 'nova-volume', u'Cloud controller fabric',
[u'OpenStack'], 1),
('man/nova-xvpvncproxy', 'nova-xvpvncproxy', u'Cloud controller fabric',
[u'OpenStack'], 1)
]

View File

@ -33,8 +33,8 @@ Below you will find a helpful explanation of the different components.
[ Auth Manager ] ---
| \- ( DB )
|
| [ scheduler ] - [ volume ] - ( iSCSI )
| /
|
|
[ Web Dashboard ] -> [ api ] -- < AMQP > ------ [ network ] - ( Flat/Vlan )
| \
< HTTP > [ scheduler ] - [ compute ] - ( libvirt/xen )
@ -46,7 +46,6 @@ Below you will find a helpful explanation of the different components.
* api: component that receives http requests, converts commands and communicates with other components via the queue or http (in the case of objectstore)
* Auth Manager: component responsible for users/projects/and roles. Can backend to DB or LDAP. This is not a separate binary, but rather a python class that is used by most components in the system.
* objectstore: http server that replicates s3 api and allows storage and retrieval of images
* scheduler: decides which host gets each vm and volume
* volume: manages dynamically attachable block devices.
* scheduler: decides which host gets each vm
* network: manages ip forwarding, bridges, and vlans
* compute: manages communication with hypervisor and virtual machines.

View File

@ -67,8 +67,6 @@ Module Reference
services
database
volume
xensmvolume
compute
network
api

View File

@ -30,12 +30,12 @@ Nova uses direct, fanout, and topic-based exchanges. The architecture looks like
..
Nova implements RPC (both request+response, and one-way, respectively nicknamed 'rpc.call' and 'rpc.cast') over AMQP by providing an adapter class which take cares of marshaling and unmarshaling of messages into function calls. Each Nova service (for example Compute, Volume, etc.) create two queues at the initialization time, one which accepts messages with routing keys 'NODE-TYPE.NODE-ID' (for example compute.hostname) and another, which accepts messages with routing keys as generic 'NODE-TYPE' (for example compute). The former is used specifically when Nova-API needs to redirect commands to a specific node like 'euca-terminate instance'. In this case, only the compute node whose host's hypervisor is running the virtual machine can kill the instance. The API acts as a consumer when RPC calls are request/response, otherwise is acts as publisher only.
Nova implements RPC (both request+response, and one-way, respectively nicknamed 'rpc.call' and 'rpc.cast') over AMQP by providing an adapter class which take cares of marshaling and unmarshaling of messages into function calls. Each Nova service (for example Compute, Scheduler, etc.) create two queues at the initialization time, one which accepts messages with routing keys 'NODE-TYPE.NODE-ID' (for example compute.hostname) and another, which accepts messages with routing keys as generic 'NODE-TYPE' (for example compute). The former is used specifically when Nova-API needs to redirect commands to a specific node like 'euca-terminate instance'. In this case, only the compute node whose host's hypervisor is running the virtual machine can kill the instance. The API acts as a consumer when RPC calls are request/response, otherwise is acts as publisher only.
Nova RPC Mappings
-----------------
The figure below shows the internals of a message broker node (referred to as a RabbitMQ node in the diagrams) when a single instance is deployed and shared in an OpenStack cloud. Every Nova component connects to the message broker and, depending on its personality (for example a compute node or a network node), may use the queue either as an Invoker (such as API or Scheduler) or a Worker (such as Compute, Volume or Network). Invokers and Workers do not actually exist in the Nova object model, but we are going to use them as an abstraction for sake of clarity. An Invoker is a component that sends messages in the queuing system via two operations: 1) rpc.call and ii) rpc.cast; a Worker is a component that receives messages from the queuing system and reply accordingly to rcp.call operations.
The figure below shows the internals of a message broker node (referred to as a RabbitMQ node in the diagrams) when a single instance is deployed and shared in an OpenStack cloud. Every Nova component connects to the message broker and, depending on its personality (for example a compute node or a network node), may use the queue either as an Invoker (such as API or Scheduler) or a Worker (such as Compute or Network). Invokers and Workers do not actually exist in the Nova object model, but we are going to use them as an abstraction for sake of clarity. An Invoker is a component that sends messages in the queuing system via two operations: 1) rpc.call and ii) rpc.cast; a Worker is a component that receives messages from the queuing system and reply accordingly to rcp.call operations.
Figure 2 shows the following internal elements:
@ -97,10 +97,8 @@ The figure below shows the status of a RabbitMQ node after Nova components' boot
2. compute
3. network.phantom (phantom is hostname)
4. network
5. volume.phantom (phantom is hostname)
6. volume
7. scheduler.phantom (phantom is hostname)
8. scheduler
5. scheduler.phantom (phantom is hostname)
6. scheduler
.. image:: /images/rpc/state.png
:width: 60%

View File

@ -1,52 +0,0 @@
..
Copyright 2010-2011 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.
Storage Volumes, Disks
======================
.. todo:: rework after iSCSI merge (see 'Old Docs') (todd or vish)
The :mod:`nova.volume.manager` Module
-------------------------------------
.. automodule:: nova.volume.manager
:noindex:
:members:
:undoc-members:
:show-inheritance:
The :mod:`nova.volume.driver` Module
-------------------------------------
.. automodule:: nova.volume.driver
:noindex:
:members:
:undoc-members:
:show-inheritance:
Tests
-----
The :mod:`volume_unittest` Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: nova.tests.volume_unittest
:noindex:
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,88 +0,0 @@
Xen Storage Manager Volume Driver
=================================
The Xen Storage Manager (xensm) driver for Nova-Volume is based on XenAPI Storage Manager. This will not only provide basic storage functionality (like volume creation, and destruction) on a number of different storage back-ends, such as Netapp, NFS, etc. but it will also enable the capability of using more sophisticated storage back-ends for operations like cloning/snapshotting etc. To have an idea of the benefits of using XenAPI SM to provide back-end storage services, the list below shows some of the storage plugins already supported in XenServer/XCP:
- NFS VHD: SR plugin which stores disks as VHD files on a remote NFS filesystem
- Local VHD on LVM: SR plugin which represents disks as VHD disks on Logical Volumes within a locally-attached Volume Group
- HBA LUN-per-VDI driver: SR plugin which represents LUNs as VDIs sourced by hardware HBA adapters, e.g. hardware-based iSCSI or FC support
- NetApp: SR driver for mapping of LUNs to VDIs on a NETAPP server, providing use of fast snapshot and clone features on the filer
- LVHD over FC: SR plugin which represents disks as VHDs on Logical Volumes within a Volume Group created on an HBA LUN, e.g. hardware-based iSCSI or FC support
- iSCSI: Base ISCSI SR driver, provides a LUN-per-VDI. Does not support creation of VDIs but accesses existing LUNs on a target.
- LVHD over iSCSI: SR plugin which represents disks as Logical Volumes within a Volume Group created on an iSCSI LUN
- EqualLogic: SR driver for mapping of LUNs to VDIs on an EQUALLOGIC array group, providing use of fast snapshot and clone features on the array
Glossary
=========
XenServer: Commercial, supported product from Citrix
Xen Cloud Platform (XCP): Open-source equivalent of XenServer (and the development project for the toolstack). Everything said about XenServer below applies equally to XCP
XenAPI: The management API exposed by XenServer and XCP
xapi: The primary daemon on XenServer and Xen Cloud Platform; the one that exposes the XenAPI
Design
=======
Definitions
-----------
Backend: A term for a particular storage backend. This could be iSCSI, NFS, Netapp etc.
Backend-config: All the parameters required to connect to a specific backend. For e.g. For NFS, this would be the server, path, etc.
Flavor: This term is equivalent to volume "types". A user friendly term to specify some notion of quality of service. For example, "gold" might mean that the volumes will use a backend where backups are possible.
A flavor can be associated with multiple backends. The volume scheduler, with the help of the driver, will decide which backend will be used to create a volume of a particular flavor. Currently, the driver uses a simple "first-fit" policy, where the first backend that can successfully create this volume is the one that is used.
Operation
----------
Using the nova-manage command detailed in the implementation, an admin can add flavors and backends.
One or more nova-volume service instances will be deployed per availability zone. When an instance is started, it will create storage repositories (SRs) to connect to the backends available within that zone. All nova-volume instances within a zone can see all the available backends. These instances are completely symmetric and hence should be able to service any create_volume request within the zone.
Commands
=========
A category called "sm" has been added to nova-manage in the class StorageManagerCommands.
The following actions will be added:
- flavor_list
- flavor_create
- flavor_delete
- backend_list
- backend_add
- backend_remove
Usage:
------
nova-manage sm flavor_create <label> <description>
nova-manage sm flavor_delete<label>
nova-manage sm backend_add <flavor label> <SR type> [config connection parameters]
Note: SR type and config connection parameters are in keeping with the Xen Command Line Interface. http://support.citrix.com/article/CTX124887
nova-manage sm backend_delete <backend-id>
Examples:
---------
nova-manage sm flavor_create gold "Not all that glitters"
nova-manage sm flavor_delete gold
nova-manage sm backend_add gold nfs name_label=toybox-renuka server=myserver serverpath=/local/scratch/myname
nova-manage sm backend_remove 1
API Changes
===========
No API changes have been introduced so far. The existing euca-create-volume and euca-delete-volume commands (or equivalent OpenStack API commands) should be used.

View File

@ -1,49 +0,0 @@
===================
nova-api-os-volume
===================
-------------------------------------------
Server for the Nova OpenStack Volume APIs
-------------------------------------------
:Author: openstack@lists.launchpad.net
:Date: 2012-09-27
:Copyright: OpenStack LLC
:Version: 2012.1
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
nova-api-os-volume [options]
DESCRIPTION
===========
nova-api-os-volume is a server daemon that serves the Nova OpenStack API
OPTIONS
=======
**General options**
FILES
========
* /etc/nova/nova.conf
* /etc/nova/api-paste.ini
* /etc/nova/policy.json
* /etc/nova/rootwrap.conf
* /etc/nova/rootwrap.d/
SEE ALSO
========
* `OpenStack Nova <http://nova.openstack.org>`__
* `OpenStack Nova <http://nova.openstack.org>`__
BUGS
====
* Nova is sourced in Launchpad so you can view current bugs at `OpenStack Nova <http://nova.openstack.org>`__

View File

@ -30,8 +30,8 @@ You also need to let the nova user run nova-rootwrap as root in sudoers:
nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap /etc/nova/rootwrap.conf *
To make allowed commands node-specific, your packaging should only
install {compute,network,volume}.filters respectively on compute, network
and volume nodes (i.e. nova-api nodes should not have any of those files
install {compute,network}.filters respectively on compute and network
nodes (i.e. nova-api nodes should not have any of those files
installed).

View File

@ -1,61 +0,0 @@
=======================
nova-volume-usage-audit
=======================
-------------------------------------------
Generate Usage Notifications for Volumes
-------------------------------------------
:Author: openstack@lists.launchpad.net
:Date: 2012-09-27
:Copyright: OpenStack LLC
:Version: 2012.1
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
nova-volume-usage-audit [options]
DESCRIPTION
===========
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.
OPTIONS
=======
**General options**
FILES
========
* /etc/nova/nova.conf
SEE ALSO
========
* `OpenStack Nova <http://nova.openstack.org>`__
* `OpenStack Nova <http://nova.openstack.org>`__
BUGS
====
* Nova is sourced in Launchpad so you can view current bugs at `OpenStack Nova <http://nova.openstack.org>`__

View File

@ -1,54 +0,0 @@
===========
nova-volume
===========
-------------------
Nova Volume Server
-------------------
:Author: openstack@lists.launchpad.net
:Date: 2012-09-27
:Copyright: OpenStack LLC
:Version: 2012.1
:Manual section: 1
:Manual group: cloud computing
SYNOPSIS
========
nova-volume [options]
DESCRIPTION
===========
nova-volume manages creating, attaching, detaching, and persistent storage.
Persistent storage volumes keep their state independent of instances. You can
attach to an instance, terminate the instance, spawn a new instance (even
one from a different image) and re-attach the volume with the same data
intact.
OPTIONS
=======
**General options**
FILES
========
* /etc/nova/nova.conf
* /etc/nova/policy.json
* /etc/nova/rootwrap.conf
* /etc/nova/rootwrap.d/
SEE ALSO
========
* `OpenStack Nova <http://nova.openstack.org>`__
* `OpenStack Nova <http://nova.openstack.org>`__
BUGS
====
* Nova is sourced in Launchpad so you can view current bugs at `OpenStack Nova <http://nova.openstack.org>`__

View File

@ -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',

View File

@ -87,20 +87,6 @@ def handle_schedule_error(context, ex, instance_uuid, request_spec):
'scheduler.run_instance', notifier.ERROR, payload)
def cast_to_volume_host(context, host, method, **kwargs):
"""Cast request to a volume host queue"""
volume_id = kwargs.get('volume_id', None)
if volume_id is not None:
now = timeutils.utcnow()
db.volume_update(context, volume_id,
{'host': host, 'scheduled_at': now})
rpc.cast(context,
rpc.queue_get_for(context, FLAGS.volume_topic, host),
{"method": method, "args": kwargs})
LOG.debug(_("Casted '%(method)s' to volume '%(host)s'") % locals())
def instance_update_db(context, instance_uuid):
'''Clear the host and set the scheduled_at field of an Instance.
@ -127,13 +113,11 @@ def cast_to_compute_host(context, host, method, **kwargs):
def cast_to_host(context, topic, host, method, **kwargs):
"""Generic cast to host"""
topic_mapping = {
FLAGS.compute_topic: cast_to_compute_host,
FLAGS.volume_topic: cast_to_volume_host}
topic_mapping = {FLAGS.compute_topic: cast_to_compute_host}
func = topic_mapping.get(topic)
if func:
func(context, host, method, **kwargs)
cast_to_compute_host(context, host, method, **kwargs)
else:
rpc.cast(context,
rpc.queue_get_for(context, topic, host),

View File

@ -88,15 +88,6 @@ service_opts = [
cfg.IntOpt('metadata_workers',
default=None,
help='Number of workers for metadata service'),
cfg.StrOpt('osapi_volume_listen',
default="0.0.0.0",
help='IP address for OpenStack Volume API to listen'),
cfg.IntOpt('osapi_volume_listen_port',
default=8776,
help='port for os volume api to listen'),
cfg.IntOpt('osapi_volume_workers',
default=None,
help='Number of workers for OpenStack Volume API service'),
]
FLAGS = flags.FLAGS

View File

@ -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)

View File

@ -175,18 +175,6 @@ class SchedulerManagerTestCase(test.TestCase):
self.manager.run_instance(self.context, request_spec,
None, None, None, None, {})
def test_create_volume_no_valid_host_puts_volume_in_error(self):
self._mox_schedule_method_helper('schedule_create_volume')
self.mox.StubOutWithMock(db, 'volume_update')
self.manager.driver.schedule_create_volume(self.context, '1', '2',
None).AndRaise(exception.NoValidHost(reason=''))
db.volume_update(self.context, '1', {'status': 'error'})
self.mox.ReplayAll()
self.assertRaises(exception.NoValidHost, self.manager.create_volume,
self.context, '1', '2')
def test_prep_resize_no_valid_host_back_in_active_state(self):
fake_instance_uuid = 'fake-instance-id'
inst = {"vm_state": "", "task_state": ""}
@ -305,13 +293,10 @@ class SchedulerTestCase(test.TestCase):
self.assertEqual(result, ['host2'])
def _live_migration_instance(self):
volume1 = {'id': 31338}
volume2 = {'id': 31339}
return {'id': 31337,
'uuid': 'fake_uuid',
'name': 'fake-instance',
'host': 'fake_host1',
'volumes': [volume1, volume2],
'power_state': power_state.RUNNING,
'memory_mb': 1024,
'root_gb': 1024,
@ -656,48 +641,6 @@ class SchedulerDriverModuleTestCase(test.TestCase):
super(SchedulerDriverModuleTestCase, self).setUp()
self.context = context.RequestContext('fake_user', 'fake_project')
def test_cast_to_volume_host_update_db_with_volume_id(self):
host = 'fake_host1'
method = 'fake_method'
fake_kwargs = {'volume_id': 31337,
'extra_arg': 'meow'}
queue = 'fake_queue'
self.mox.StubOutWithMock(timeutils, 'utcnow')
self.mox.StubOutWithMock(db, 'volume_update')
self.mox.StubOutWithMock(rpc, 'queue_get_for')
self.mox.StubOutWithMock(rpc, 'cast')
timeutils.utcnow().AndReturn('fake-now')
db.volume_update(self.context, 31337,
{'host': host, 'scheduled_at': 'fake-now'})
rpc.queue_get_for(self.context, 'volume', host).AndReturn(queue)
rpc.cast(self.context, queue,
{'method': method,
'args': fake_kwargs})
self.mox.ReplayAll()
driver.cast_to_volume_host(self.context, host, method,
**fake_kwargs)
def test_cast_to_volume_host_update_db_without_volume_id(self):
host = 'fake_host1'
method = 'fake_method'
fake_kwargs = {'extra_arg': 'meow'}
queue = 'fake_queue'
self.mox.StubOutWithMock(rpc, 'queue_get_for')
self.mox.StubOutWithMock(rpc, 'cast')
rpc.queue_get_for(self.context, 'volume', host).AndReturn(queue)
rpc.cast(self.context, queue,
{'method': method,
'args': fake_kwargs})
self.mox.ReplayAll()
driver.cast_to_volume_host(self.context, host, method,
**fake_kwargs)
def test_cast_to_compute_host_update_db_with_instance_uuid(self):
host = 'fake_host1'
method = 'fake_method'
@ -753,19 +696,6 @@ class SchedulerDriverModuleTestCase(test.TestCase):
driver.cast_to_host(self.context, 'compute', host, method,
**fake_kwargs)
def test_cast_to_host_volume_topic(self):
host = 'fake_host1'
method = 'fake_method'
fake_kwargs = {'extra_arg': 'meow'}
self.mox.StubOutWithMock(driver, 'cast_to_volume_host')
driver.cast_to_volume_host(self.context, host, method,
**fake_kwargs)
self.mox.ReplayAll()
driver.cast_to_host(self.context, 'volume', host, method,
**fake_kwargs)
def test_cast_to_host_unknown_topic(self):
host = 'fake_host1'
method = 'fake_method'

View File

@ -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'])

View File

@ -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}

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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()

View File

@ -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'))

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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})

View File

@ -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"})

View File

@ -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'], {})

View File

@ -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'])

View File

@ -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)

View File

@ -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']))

View File

@ -49,7 +49,6 @@ setuptools.setup(name='nova',
'bin/nova-api-ec2',
'bin/nova-api-metadata',
'bin/nova-api-os-compute',
'bin/nova-api-os-volume',
'bin/nova-rpc-zmq-receiver',
'bin/nova-cert',
'bin/nova-clear-rabbit-queues',
@ -63,8 +62,6 @@ setuptools.setup(name='nova',
'bin/nova-objectstore',
'bin/nova-rootwrap',
'bin/nova-scheduler',
'bin/nova-volume',
'bin/nova-volume-usage-audit',
'bin/nova-xvpvncproxy',
],
py_modules=[])