summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorBrian Waldon <bcwaldon@gmail.com>2012-03-14 17:24:07 -0700
committerChris Behrens <cbehrens@codestud.com>2012-03-15 20:35:09 -0700
commita3bab242db16d036a415aa6ce3e95803b001bcc6 (patch)
tree218f2afc3869757689683e776853cb1d45dfc5fc /nova
parent30b8e35e80486b26eeb71bc62d92bae240cb72f2 (diff)
Remove Virtual Storage Array (VSA) code
* Remove core vsa code (api, manager, drivers) * Remove nova-vsa executable * Remove OpenStack Compute API v2 vsa extension * Remove vsa scheduler * Remove vsa db api methods * Remove Zadara volume driver * Do not migrate out any existing data * Fixes bug 954490 Change-Id: Idab3d60796d5edbc23ef9f0887fcc1af558c6215
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/compute/contrib/virtual_storage_arrays.py701
-rw-r--r--nova/db/api.py36
-rw-r--r--nova/db/sqlalchemy/api.py82
-rw-r--r--nova/db/sqlalchemy/models.py34
-rw-r--r--nova/exception.py12
-rw-r--r--nova/flags.py18
-rwxr-xr-xnova/rootwrap/volume.py3
-rw-r--r--nova/scheduler/vsa.py532
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_vsa.py637
-rw-r--r--nova/tests/api/openstack/compute/test_extensions.py1
-rw-r--r--nova/tests/api/openstack/fakes.py9
-rw-r--r--nova/tests/scheduler/test_vsa_scheduler.py626
-rw-r--r--nova/tests/test_vsa.py171
-rw-r--r--nova/tests/test_vsa_volumes.py133
-rw-r--r--nova/volume/driver.py322
-rw-r--r--nova/volume/manager.py21
-rw-r--r--nova/volume/volume_types.py21
-rw-r--r--nova/vsa/__init__.py16
-rw-r--r--nova/vsa/api.py412
-rw-r--r--nova/vsa/connection.py25
-rw-r--r--nova/vsa/fake.py22
-rw-r--r--nova/vsa/manager.py181
-rw-r--r--nova/vsa/utils.py80
23 files changed, 1 insertions, 4094 deletions
diff --git a/nova/api/openstack/compute/contrib/virtual_storage_arrays.py b/nova/api/openstack/compute/contrib/virtual_storage_arrays.py
deleted file mode 100644
index 9c413e76f..000000000
--- a/nova/api/openstack/compute/contrib/virtual_storage_arrays.py
+++ /dev/null
@@ -1,701 +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.
-
-""" The virtul storage array extension"""
-
-
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack.compute.contrib import volumes
-from nova.api.openstack import extensions
-from nova.api.openstack.compute import servers
-from nova.api.openstack import wsgi
-from nova.api.openstack import xmlutil
-from nova import compute
-from nova.compute import instance_types
-from nova import network
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova.vsa import api as vsa_api
-from nova import volume
-
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-authorize = extensions.extension_authorizer('compute',
- 'virtual_storage_arrays')
-
-
-def _vsa_view(context, vsa, details=False, instances=None):
- """Map keys for vsa summary/detailed view."""
- d = {}
-
- d['id'] = vsa.get('id')
- d['name'] = vsa.get('name')
- d['displayName'] = vsa.get('display_name')
- d['displayDescription'] = vsa.get('display_description')
-
- d['createTime'] = vsa.get('created_at')
- d['status'] = vsa.get('status')
-
- if 'vsa_instance_type' in vsa:
- d['vcType'] = vsa['vsa_instance_type'].get('name', None)
- else:
- d['vcType'] = vsa['instance_type_id']
-
- d['vcCount'] = vsa.get('vc_count')
- d['driveCount'] = vsa.get('vol_count')
-
- d['ipAddress'] = None
- for instance in instances:
- fixed_addr = None
- floating_addr = None
- if instance['fixed_ips']:
- fixed = instance['fixed_ips'][0]
- fixed_addr = fixed['address']
- if fixed['floating_ips']:
- floating_addr = fixed['floating_ips'][0]['address']
-
- if floating_addr:
- d['ipAddress'] = floating_addr
- break
- else:
- d['ipAddress'] = d['ipAddress'] or fixed_addr
-
- return d
-
-
-def make_vsa(elem):
- elem.set('id')
- elem.set('name')
- elem.set('displayName')
- elem.set('displayDescription')
- elem.set('createTime')
- elem.set('status')
- elem.set('vcType')
- elem.set('vcCount')
- elem.set('driveCount')
- elem.set('ipAddress')
-
-
-class VsaTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('vsa', selector='vsa')
- make_vsa(root)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaSetTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('vsaSet')
- elem = xmlutil.SubTemplateElement(root, 'vsa', selector='vsaSet')
- make_vsa(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaController(object):
- """The Virtual Storage Array API controller for the OpenStack API."""
-
- def __init__(self):
- self.vsa_api = vsa_api.API()
- self.compute_api = compute.API()
- self.network_api = network.API()
- super(VsaController, self).__init__()
-
- def _get_instances_by_vsa_id(self, context, id):
- return self.compute_api.get_all(context,
- search_opts={'metadata': dict(vsa_id=str(id))})
-
- def _items(self, req, details):
- """Return summary or detailed list of VSAs."""
- context = req.environ['nova.context']
- authorize(context)
- vsas = self.vsa_api.get_all(context)
- limited_list = common.limited(vsas, req)
-
- vsa_list = []
- for vsa in limited_list:
- instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
- vsa_list.append(_vsa_view(context, vsa, details, instances))
- return {'vsaSet': vsa_list}
-
- @wsgi.serializers(xml=VsaSetTemplate)
- def index(self, req):
- """Return a short list of VSAs."""
- return self._items(req, details=False)
-
- @wsgi.serializers(xml=VsaSetTemplate)
- def detail(self, req):
- """Return a detailed list of VSAs."""
- return self._items(req, details=True)
-
- @wsgi.serializers(xml=VsaTemplate)
- def show(self, req, id):
- """Return data about the given VSA."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vsa = self.vsa_api.get(context, vsa_id=id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
- return {'vsa': _vsa_view(context, vsa, True, instances)}
-
- @wsgi.serializers(xml=VsaTemplate)
- def create(self, req, body):
- """Create a new VSA."""
- context = req.environ['nova.context']
- authorize(context)
-
- if not body or 'vsa' not in body:
- LOG.debug(_("No body provided"), context=context)
- raise exc.HTTPUnprocessableEntity()
-
- vsa = body['vsa']
-
- display_name = vsa.get('displayName')
- vc_type = vsa.get('vcType', FLAGS.default_vsa_instance_type)
- try:
- instance_type = instance_types.get_instance_type_by_name(vc_type)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- LOG.audit(_("Create VSA %(display_name)s of type %(vc_type)s"),
- locals(), context=context)
-
- _vsa_placement = vsa.get('placement', {})
- args = dict(display_name=display_name,
- display_description=vsa.get('displayDescription'),
- instance_type=instance_type,
- storage=vsa.get('storage'),
- shared=vsa.get('shared'),
- availability_zone=_vsa_placement.get('AvailabilityZone'))
-
- vsa = self.vsa_api.create(context, **args)
-
- instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
- return {'vsa': _vsa_view(context, vsa, True, instances)}
-
- def delete(self, req, id):
- """Delete a VSA."""
- context = req.environ['nova.context']
- authorize(context)
-
- LOG.audit(_("Delete VSA with id: %s"), id, context=context)
-
- try:
- self.vsa_api.delete(context, vsa_id=id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- def associate_address(self, req, id, body):
- """ /zadr-vsa/{vsa_id}/associate_address
- auto or manually associate an IP to VSA
- """
- context = req.environ['nova.context']
- authorize(context)
-
- # FIXME(comstud): Seems like this always assigns 'auto' right
- # now despite what the docstring says this should support.
- if body is None:
- ip = 'auto'
- else:
- ip = body.get('ipAddress', 'auto')
-
- LOG.audit(_("Associate address %(ip)s to VSA %(id)s"),
- locals(), context=context)
-
- try:
- instances = self._get_instances_by_vsa_id(context, id)
- if instances is None or len(instances) == 0:
- raise exc.HTTPNotFound()
-
- for instance in instances:
- self.network_api.allocate_for_instance(context, instance,
- vpn=False)
- # Placeholder
- return
-
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- def disassociate_address(self, req, id, body):
- """ /zadr-vsa/{vsa_id}/disassociate_address
- auto or manually associate an IP to VSA
- """
- context = req.environ['nova.context']
- authorize(context)
-
- if body is None:
- ip = 'auto'
- else:
- ip = body.get('ipAddress', 'auto')
-
- LOG.audit(_("Disassociate address from VSA %(id)s"),
- locals(), context=context)
- # Placeholder
-
-
-def make_volume(elem):
- volumes.make_volume(elem)
- elem.set('name')
- elem.set('vsaId')
-
-
-class VsaVolumeDriveController(volumes.VolumeController):
- """The base class for VSA volumes & drives.
-
- A child resource of the VSA object. Allows operations with
- volumes and drives created to/from particular VSA
-
- """
-
- def __init__(self):
- self.volume_api = volume.API()
- self.vsa_api = vsa_api.API()
- super(VsaVolumeDriveController, self).__init__()
-
- def _translation(self, context, vol, vsa_id, details):
- if details:
- translation = volumes._translate_volume_detail_view
- else:
- translation = volumes._translate_volume_summary_view
-
- d = translation(context, vol)
- d['vsaId'] = vsa_id
- d['name'] = vol['name']
- return d
-
- def _check_volume_ownership(self, context, vsa_id, id):
- obj = self.object
- try:
- volume_ref = self.volume_api.get(context, id)
- except exception.NotFound:
- LOG.error(_("%(obj)s with ID %(id)s not found"), locals())
- raise
-
- own_vsa_id = self.volume_api.get_volume_metadata_value(volume_ref,
- self.direction)
- if own_vsa_id != vsa_id:
- LOG.error(_("%(obj)s with ID %(id)s belongs to VSA %(own_vsa_id)s"
- " and not to VSA %(vsa_id)s."), locals())
- raise exception.Invalid()
-
- def _items(self, req, vsa_id, details):
- """Return summary or detailed list of volumes for particular VSA."""
- context = req.environ['nova.context']
- authorize(context)
-
- vols = self.volume_api.get_all(context,
- search_opts={'metadata': {self.direction: str(vsa_id)}})
- limited_list = common.limited(vols, req)
-
- res = [self._translation(context, vol, vsa_id, details)
- for vol in limited_list]
-
- return {self.objects: res}
-
- def index(self, req, vsa_id):
- """Return a short list of volumes created from particular VSA."""
- LOG.audit(_("Index. vsa_id=%(vsa_id)s"), locals())
- return self._items(req, vsa_id, details=False)
-
- def detail(self, req, vsa_id):
- """Return a detailed list of volumes created from particular VSA."""
- LOG.audit(_("Detail. vsa_id=%(vsa_id)s"), locals())
- return self._items(req, vsa_id, details=True)
-
- def create(self, req, vsa_id, body):
- """Create a new volume from VSA."""
- LOG.audit(_("Create. vsa_id=%(vsa_id)s, body=%(body)s"), locals())
- context = req.environ['nova.context']
- authorize(context)
-
- if not body:
- raise exc.HTTPUnprocessableEntity()
-
- vol = body[self.object]
- size = vol['size']
- LOG.audit(_("Create volume of %(size)s GB from VSA ID %(vsa_id)s"),
- locals(), context=context)
- try:
- # create is supported for volumes only (drives created through VSA)
- volume_type = self.vsa_api.get_vsa_volume_type(context)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- new_volume = self.volume_api.create(context,
- size,
- vol.get('displayName'),
- vol.get('displayDescription'),
- None,
- volume_type=volume_type,
- metadata=dict(from_vsa_id=str(vsa_id)))
-
- return {self.object: self._translation(context, new_volume,
- vsa_id, True)}
-
- def update(self, req, vsa_id, id, body):
- """Update a volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- self._check_volume_ownership(context, vsa_id, id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- except exception.Invalid:
- raise exc.HTTPBadRequest()
-
- vol = body[self.object]
- updatable_fields = [{'displayName': 'display_name'},
- {'displayDescription': 'display_description'},
- {'status': 'status'},
- {'providerLocation': 'provider_location'},
- {'providerAuth': 'provider_auth'}]
- changes = {}
- for field in updatable_fields:
- key = field.keys()[0]
- val = field[key]
- if key in vol:
- changes[val] = vol[key]
-
- obj = self.object
- LOG.audit(_("Update %(obj)s with id: %(id)s, changes: %(changes)s"),
- locals(), context=context)
-
- try:
- volume = self.volume_api.get(context, id)
- self.volume_api.update(context, volume, fields=changes)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- return webob.Response(status_int=202)
-
- def delete(self, req, vsa_id, id):
- """Delete a volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- LOG.audit(_("Delete. vsa_id=%(vsa_id)s, id=%(id)s"), locals())
-
- try:
- self._check_volume_ownership(context, vsa_id, id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- except exception.Invalid:
- raise exc.HTTPBadRequest()
-
- return super(VsaVolumeDriveController, self).delete(req, id)
-
- def show(self, req, vsa_id, id):
- """Return data about the given volume."""
- context = req.environ['nova.context']
- authorize(context)
-
- LOG.audit(_("Show. vsa_id=%(vsa_id)s, id=%(id)s"), locals())
-
- try:
- self._check_volume_ownership(context, vsa_id, id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- except exception.Invalid:
- raise exc.HTTPBadRequest()
-
- return super(VsaVolumeDriveController, self).show(req, id)
-
-
-class VsaVolumeTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('volume', selector='volume')
- make_volume(root)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaVolumesTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('volumes')
- elem = xmlutil.SubTemplateElement(root, 'volume', selector='volumes')
- make_volume(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaVolumeController(VsaVolumeDriveController):
- """The VSA volume API controller for the OpenStack API.
-
- A child resource of the VSA object. Allows operations with volumes created
- by particular VSA
-
- """
-
- def __init__(self):
- self.direction = 'from_vsa_id'
- self.objects = 'volumes'
- self.object = 'volume'
- super(VsaVolumeController, self).__init__()
-
- @wsgi.serializers(xml=VsaVolumesTemplate)
- def index(self, req, vsa_id):
- return super(VsaVolumeController, self).index(req, vsa_id)
-
- @wsgi.serializers(xml=VsaVolumesTemplate)
- def detail(self, req, vsa_id):
- return super(VsaVolumeController, self).detail(req, vsa_id)
-
- @wsgi.serializers(xml=VsaVolumeTemplate)
- def create(self, req, vsa_id, body):
- return super(VsaVolumeController, self).create(req, vsa_id, body)
-
- @wsgi.serializers(xml=VsaVolumeTemplate)
- def update(self, req, vsa_id, id, body):
- return super(VsaVolumeController, self).update(req, vsa_id, id, body)
-
- @wsgi.serializers(xml=VsaVolumeTemplate)
- def show(self, req, vsa_id, id):
- return super(VsaVolumeController, self).show(req, vsa_id, id)
-
-
-class VsaDriveTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('drive', selector='drive')
- make_volume(root)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaDrivesTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('drives')
- elem = xmlutil.SubTemplateElement(root, 'drive', selector='drives')
- make_volume(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaDriveController(VsaVolumeDriveController):
- """The VSA Drive API controller for the OpenStack API.
-
- A child resource of the VSA object. Allows operations with drives created
- for particular VSA
-
- """
-
- def __init__(self):
- self.direction = 'to_vsa_id'
- self.objects = 'drives'
- self.object = 'drive'
- super(VsaDriveController, self).__init__()
-
- def create(self, req, vsa_id, body):
- """Create a new drive for VSA. Should be done through VSA APIs"""
- raise exc.HTTPBadRequest()
-
- def update(self, req, vsa_id, id, body):
- """Update a drive. Should be done through VSA APIs"""
- raise exc.HTTPBadRequest()
-
- def delete(self, req, vsa_id, id):
- """Delete a volume. Should be done through VSA APIs"""
- raise exc.HTTPBadRequest()
-
- @wsgi.serializers(xml=VsaDrivesTemplate)
- def index(self, req, vsa_id):
- return super(VsaDriveController, self).index(req, vsa_id)
-
- @wsgi.serializers(xml=VsaDrivesTemplate)
- def detail(self, req, vsa_id):
- return super(VsaDriveController, self).detail(req, vsa_id)
-
- @wsgi.serializers(xml=VsaDriveTemplate)
- def show(self, req, vsa_id, id):
- return super(VsaDriveController, self).show(req, vsa_id, id)
-
-
-def make_vpool(elem):
- elem.set('id')
- elem.set('vsaId')
- elem.set('name')
- elem.set('displayName')
- elem.set('displayDescription')
- elem.set('driveCount')
- elem.set('protection')
- elem.set('stripeSize')
- elem.set('stripeWidth')
- elem.set('createTime')
- elem.set('status')
-
- drive_ids = xmlutil.SubTemplateElement(elem, 'driveIds')
- drive_id = xmlutil.SubTemplateElement(drive_ids, 'driveId',
- selector='driveIds')
- drive_id.text = xmlutil.Selector()
-
-
-class VsaVPoolTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('vpool', selector='vpool')
- make_vpool(root)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaVPoolsTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('vpools')
- elem = xmlutil.SubTemplateElement(root, 'vpool', selector='vpools')
- make_vpool(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VsaVPoolController(object):
- """The vPool VSA API controller for the OpenStack API."""
-
- def __init__(self):
- self.vsa_api = vsa_api.API()
- super(VsaVPoolController, self).__init__()
-
- @wsgi.serializers(xml=VsaVPoolsTemplate)
- def index(self, req, vsa_id):
- """Return a short list of vpools created from particular VSA."""
- return {'vpools': []}
-
- def create(self, req, vsa_id, body):
- """Create a new vPool for VSA."""
- raise exc.HTTPBadRequest()
-
- def update(self, req, vsa_id, id, body):
- """Update vPool parameters."""
- raise exc.HTTPBadRequest()
-
- def delete(self, req, vsa_id, id):
- """Delete a vPool."""
- raise exc.HTTPBadRequest()
-
- def show(self, req, vsa_id, id):
- """Return data about the given vPool."""
- raise exc.HTTPBadRequest()
-
-
-class VsaVCController(servers.Controller):
- """The VSA Virtual Controller API controller for the OpenStack API."""
-
- def __init__(self):
- self.vsa_api = vsa_api.API()
- self.compute_api = compute.API()
- self.vsa_id = None # VP-TODO(vladimir.p): temporary ugly hack
- super(VsaVCController, self).__init__()
-
- def _get_servers(self, req, is_detail):
- """Returns a list of servers, taking into account any search
- options specified.
- """
-
- if self.vsa_id is None:
- super(VsaVCController, self)._get_servers(req, is_detail)
-
- context = req.environ['nova.context']
-
- search_opts = {'metadata': dict(vsa_id=str(self.vsa_id))}
- instance_list = self.compute_api.get_all(
- context, search_opts=search_opts)
-
- limited_list = self._limit_items(instance_list, req)
- servers = [self._build_view(req, inst, is_detail)['server']
- for inst in limited_list]
- return dict(servers=servers)
-
- @wsgi.serializers(xml=servers.MinimalServersTemplate)
- def index(self, req, vsa_id):
- """Return list of instances for particular VSA."""
-
- LOG.audit(_("Index instances for VSA %s"), vsa_id)
-
- self.vsa_id = vsa_id # VP-TODO(vladimir.p): temporary ugly hack
- result = super(VsaVCController, self).detail(req)
- self.vsa_id = None
- return result
-
- def create(self, req, vsa_id, body):
- """Create a new instance for VSA."""
- raise exc.HTTPBadRequest()
-
- def update(self, req, vsa_id, id, body):
- """Update VSA instance."""
- raise exc.HTTPBadRequest()
-
- def delete(self, req, vsa_id, id):
- """Delete VSA instance."""
- raise exc.HTTPBadRequest()
-
- @wsgi.serializers(xml=servers.ServerTemplate)
- def show(self, req, vsa_id, id):
- """Return data about the given instance."""
- return super(VsaVCController, self).show(req, id)
-
-
-class Virtual_storage_arrays(extensions.ExtensionDescriptor):
- """Virtual Storage Arrays support"""
-
- name = "VSAs"
- alias = "zadr-vsa"
- namespace = "http://docs.openstack.org/compute/ext/vsa/api/v1.1"
- updated = "2011-08-25T00:00:00+00:00"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'zadr-vsa',
- VsaController(),
- collection_actions={'detail': 'GET'},
- member_actions={'add_capacity': 'POST',
- 'remove_capacity': 'POST',
- 'associate_address': 'POST',
- 'disassociate_address': 'POST'})
- resources.append(res)
-
- res = extensions.ResourceExtension('volumes',
- VsaVolumeController(),
- collection_actions={'detail': 'GET'},
- parent=dict(
- member_name='vsa',
- collection_name='zadr-vsa'))
- resources.append(res)
-
- res = extensions.ResourceExtension('drives',
- VsaDriveController(),
- collection_actions={'detail': 'GET'},
- parent=dict(
- member_name='vsa',
- collection_name='zadr-vsa'))
- resources.append(res)
-
- res = extensions.ResourceExtension('vpools',
- VsaVPoolController(),
- parent=dict(
- member_name='vsa',
- collection_name='zadr-vsa'))
- resources.append(res)
-
- res = extensions.ResourceExtension('instances',
- VsaVCController(),
- parent=dict(
- member_name='vsa',
- collection_name='zadr-vsa'))
- resources.append(res)
-
- return resources
diff --git a/nova/db/api.py b/nova/db/api.py
index e569a52ab..286fdc8d0 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -65,9 +65,6 @@ db_opts = [
cfg.StrOpt('snapshot_name_template',
default='snapshot-%08x',
help='Template string to be used to generate snapshot names'),
- cfg.StrOpt('vsa_name_template',
- default='vsa-%08x',
- help='Template string to be used to generate VSA names'),
]
FLAGS = flags.FLAGS
@@ -1571,39 +1568,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id,
extra_specs)
-####################
-
-
-def vsa_create(context, values):
- """Creates Virtual Storage Array record."""
- return IMPL.vsa_create(context, values)
-
-
-def vsa_update(context, vsa_id, values):
- """Updates Virtual Storage Array record."""
- return IMPL.vsa_update(context, vsa_id, values)
-
-
-def vsa_destroy(context, vsa_id):
- """Deletes Virtual Storage Array record."""
- return IMPL.vsa_destroy(context, vsa_id)
-
-
-def vsa_get(context, vsa_id):
- """Get Virtual Storage Array record by ID."""
- return IMPL.vsa_get(context, vsa_id)
-
-
-def vsa_get_all(context):
- """Get all Virtual Storage Array records."""
- return IMPL.vsa_get_all(context)
-
-
-def vsa_get_all_by_project(context, project_id):
- """Get all Virtual Storage Array records by project ID."""
- return IMPL.vsa_get_all_by_project(context, project_id)
-
-
###################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 71a17584e..49810de22 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -3851,88 +3851,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id,
####################
-def _vsa_get_query(context, session=None, project_only=False):
- return model_query(context, models.VirtualStorageArray, session=session,
- project_only=project_only).\
- options(joinedload('vsa_instance_type'))
-
-
-@require_admin_context
-def vsa_create(context, values):
- """
- Creates Virtual Storage Array record.
- """
- try:
- vsa_ref = models.VirtualStorageArray()
- vsa_ref.update(values)
- vsa_ref.save()
- except Exception, e:
- raise exception.DBError(e)
- return vsa_ref
-
-
-@require_admin_context
-def vsa_update(context, vsa_id, values):
- """
- Updates Virtual Storage Array record.
- """
- session = get_session()
- with session.begin():
- vsa_ref = vsa_get(context, vsa_id, session=session)
- vsa_ref.update(values)
- vsa_ref.save(session=session)
- return vsa_ref
-
-
-@require_admin_context
-def vsa_destroy(context, vsa_id):
- """
- Deletes Virtual Storage Array record.
- """
- session = get_session()
- with session.begin():
- session.query(models.VirtualStorageArray).\
- filter_by(id=vsa_id).\
- update({'deleted': True,
- 'deleted_at': utils.utcnow(),
- 'updated_at': literal_column('updated_at')})
-
-
-@require_context
-def vsa_get(context, vsa_id, session=None):
- """
- Get Virtual Storage Array record by ID.
- """
- result = _vsa_get_query(context, session=session, project_only=True).\
- filter_by(id=vsa_id).\
- first()
-
- if not result:
- raise exception.VirtualStorageArrayNotFound(id=vsa_id)
-
- return result
-
-
-@require_admin_context
-def vsa_get_all(context):
- """
- Get all Virtual Storage Array records.
- """
- return _vsa_get_query(context).all()
-
-
-@require_context
-def vsa_get_all_by_project(context, project_id):
- """
- Get all Virtual Storage Array records by project ID.
- """
- authorize_project_context(context, project_id)
- return _vsa_get_query(context).filter_by(project_id=project_id).all()
-
-
-####################
-
-
def s3_image_get(context, image_id):
"""Find local s3 image represented by the provided id"""
result = model_query(context, models.S3Image, read_deleted="yes").\
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 8a3188ffa..3865cd5d0 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -300,32 +300,6 @@ class InstanceInfoCache(BASE, NovaBase):
primaryjoin=instance_id == Instance.uuid)
-class VirtualStorageArray(BASE, NovaBase):
- """
- Represents a virtual storage array supplying block storage to instances.
- """
- __tablename__ = 'virtual_storage_arrays'
-
- id = Column(Integer, primary_key=True, autoincrement=True)
-
- @property
- def name(self):
- return FLAGS.vsa_name_template % self.id
-
- # User editable field for display in user-facing UIs
- display_name = Column(String(255))
- display_description = Column(String(255))
-
- project_id = Column(String(255))
- availability_zone = Column(String(255))
-
- instance_type_id = Column(Integer, ForeignKey('instance_types.id'))
- image_ref = Column(String(255))
- vc_count = Column(Integer, default=0) # number of requested VC instances
- vol_count = Column(Integer, default=0) # total number of BE volumes
- status = Column(String(255))
-
-
class InstanceActions(BASE, NovaBase):
"""Represents a guest VM's actions and results"""
__tablename__ = "instance_actions"
@@ -357,13 +331,6 @@ class InstanceTypes(BASE, NovaBase):
'InstanceTypes.id, '
'InstanceTypes.deleted == False)')
- vsas = relationship(VirtualStorageArray,
- backref=backref('vsa_instance_type', uselist=False),
- foreign_keys=id,
- primaryjoin='and_('
- 'VirtualStorageArray.instance_type_id == '
- 'InstanceTypes.id, InstanceTypes.deleted == False)')
-
class Volume(BASE, NovaBase):
"""Represents a block storage device that can be attached to a vm."""
@@ -1047,7 +1014,6 @@ def register_models():
SMFlavors,
SMVolume,
User,
- VirtualStorageArray,
Volume,
VolumeMetadata,
VolumeTypeExtraSpecs,
diff --git a/nova/exception.py b/nova/exception.py
index d111e4a3d..0e961730d 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -906,18 +906,6 @@ class PasteAppNotFound(NotFound):
message = _("Could not load paste app '%(name)s' from %(path)s")
-class VSANovaAccessParamNotFound(Invalid):
- message = _("Nova access parameters were not specified.")
-
-
-class VirtualStorageArrayNotFound(NotFound):
- message = _("Virtual Storage Array %(id)d could not be found.")
-
-
-class VirtualStorageArrayNotFoundByName(NotFound):
- message = _("Virtual Storage Array %(name)s could not be found.")
-
-
class CannotResizeToSameSize(NovaException):
message = _("When resizing, instances must change size!")
diff --git a/nova/flags.py b/nova/flags.py
index 3fbee27a7..d669e23f9 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -185,9 +185,6 @@ global_opts = [
cfg.StrOpt('network_topic',
default='network',
help='the topic network nodes listen on'),
- cfg.StrOpt('vsa_topic',
- default='vsa',
- help='the topic that nova-vsa service listens on'),
cfg.StrOpt('rabbit_host',
default='localhost',
help='the RabbitMQ host'),
@@ -351,21 +348,6 @@ global_opts = [
cfg.StrOpt('scheduler_manager',
default='nova.scheduler.manager.SchedulerManager',
help='full class name for the Manager for scheduler'),
- cfg.StrOpt('vsa_manager',
- default='nova.vsa.manager.VsaManager',
- help='full class name for the Manager for VSA'),
- cfg.StrOpt('vc_image_name',
- default='vc_image',
- help='the VC image ID (for a VC image that exists in Glance)'),
- cfg.StrOpt('default_vsa_instance_type',
- default='m1.small',
- help='default instance type for VSA instances'),
- cfg.IntOpt('max_vcs_in_vsa',
- default=32,
- help='maxinum VCs in a VSA'),
- cfg.IntOpt('vsa_part_size_gb',
- default=100,
- help='default partition size for shared capacity'),
cfg.StrOpt('firewall_driver',
default='nova.virt.firewall.IptablesFirewallDriver',
help='Firewall driver (defaults to iptables)'),
diff --git a/nova/rootwrap/volume.py b/nova/rootwrap/volume.py
index 71049936b..859777aae 100755
--- a/nova/rootwrap/volume.py
+++ b/nova/rootwrap/volume.py
@@ -42,7 +42,4 @@ filterlist = [
# nova/volume/driver.py: 'iscsiadm', '-m', 'discovery', '-t',...
# nova/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ...
filters.CommandFilter("/sbin/iscsiadm", "root"),
-
- # nova/volume/driver.py:'/var/lib/zadara/bin/zadara_sncfg', *
- # sudoers does not allow zadara_sncfg yet
]
diff --git a/nova/scheduler/vsa.py b/nova/scheduler/vsa.py
deleted file mode 100644
index 275bc7581..000000000
--- a/nova/scheduler/vsa.py
+++ /dev/null
@@ -1,532 +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.
-
-"""
-VSA Simple Scheduler
-"""
-
-from nova import context
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova.openstack.common import cfg
-from nova import rpc
-from nova import utils
-from nova.scheduler import driver
-from nova.scheduler import simple
-from nova.volume import volume_types
-from nova.vsa import api as vsa_api
-
-
-LOG = logging.getLogger(__name__)
-
-vsa_scheduler_opts = [
- cfg.IntOpt('drive_type_approx_capacity_percent',
- default=10,
- help='The percentage range for capacity comparison'),
- cfg.IntOpt('vsa_unique_hosts_per_alloc',
- default=10,
- help='The number of unique hosts per storage allocation'),
- cfg.BoolOpt('vsa_select_unique_drives',
- default=True,
- help='Allow selection of same host for multiple drives'),
- ]
-
-FLAGS = flags.FLAGS
-FLAGS.register_opts(vsa_scheduler_opts)
-
-
-def BYTES_TO_GB(bytes):
- return bytes >> 30
-
-
-def GB_TO_BYTES(gb):
- return gb << 30
-
-
-class VsaScheduler(simple.SimpleScheduler):
- """Implements Scheduler for volume placement."""
-
- def __init__(self, *args, **kwargs):
- super(VsaScheduler, self).__init__(*args, **kwargs)
- self._notify_all_volume_hosts("startup")
-
- def _notify_all_volume_hosts(self, event):
- rpc.fanout_cast(context.get_admin_context(),
- FLAGS.volume_topic,
- {"method": "notification",
- "args": {"event": event}})
-
- def _qosgrp_match(self, drive_type, qos_values):
-
- def _compare_names(str1, str2):
- return str1.lower() == str2.lower()
-
- def _compare_sizes_approxim(cap_capacity, size):
- cap_capacity = BYTES_TO_GB(int(cap_capacity))
- size = int(size)
- size_perc = size * FLAGS.drive_type_approx_capacity_percent / 100
-
- return (cap_capacity >= size - size_perc and
- cap_capacity <= size + size_perc)
-
- # Add more entries for additional comparisons
- compare_list = [{'cap1': 'DriveType',
- 'cap2': 'type',
- 'cmp_func': _compare_names},
- {'cap1': 'DriveCapacity',
- 'cap2': 'size',
- 'cmp_func': _compare_sizes_approxim}]
-
- for cap in compare_list:
- if (cap['cap1'] in qos_values.keys() and
- cap['cap2'] in drive_type.keys() and
- cap['cmp_func'] is not None and
- cap['cmp_func'](qos_values[cap['cap1']],
- drive_type[cap['cap2']])):
- pass
- else:
- return False
- return True
-
- def _get_service_states(self):
- return self.host_manager.service_states
-
- def _filter_hosts(self, topic, request_spec, host_list=None):
-
- LOG.debug(_("_filter_hosts: %(request_spec)s"), locals())
-
- drive_type = request_spec['drive_type']
- LOG.debug(_("Filter hosts for drive type %s"), drive_type['name'])
-
- if host_list is None:
- host_list = self._get_service_states().iteritems()
-
- filtered_hosts = [] # returns list of (hostname, capability_dict)
- for host, host_dict in host_list:
- for service_name, service_dict in host_dict.iteritems():
- if service_name != topic:
- continue
-
- gos_info = service_dict.get('drive_qos_info', {})
- for qosgrp, qos_values in gos_info.iteritems():
- if self._qosgrp_match(drive_type, qos_values):
- if qos_values['AvailableCapacity'] > 0:
- filtered_hosts.append((host, gos_info))
- else:
- LOG.debug(_("Host %s has no free capacity. Skip"),
- host)
- break
-
- host_names = [item[0] for item in filtered_hosts]
- LOG.debug(_("Filter hosts: %s"), host_names)
- return filtered_hosts
-
- def _allowed_to_use_host(self, host, selected_hosts, unique):
- if not unique or host not in [item[0] for item in selected_hosts]:
- return True
- else:
- return False
-
- def _add_hostcap_to_list(self, selected_hosts, host, cap):
- if host not in [item[0] for item in selected_hosts]:
- selected_hosts.append((host, cap))
-
- def host_selection_algorithm(self, request_spec, all_hosts,
- selected_hosts, unique):
- """Must override this method for VSA scheduler to work."""
- raise NotImplementedError(_("Must implement host selection mechanism"))
-
- def _select_hosts(self, request_spec, all_hosts, selected_hosts=None):
-
- if selected_hosts is None:
- selected_hosts = []
-
- host = None
- if len(selected_hosts) >= FLAGS.vsa_unique_hosts_per_alloc:
- # try to select from already selected hosts only
- LOG.debug(_("Maximum number of hosts selected (%d)"),
- len(selected_hosts))
- unique = False
- (host, qos_cap) = self.host_selection_algorithm(request_spec,
- selected_hosts,
- selected_hosts,
- unique)
-
- LOG.debug(_("Selected excessive host %(host)s"), locals())
- else:
- unique = FLAGS.vsa_select_unique_drives
-
- if host is None:
- # if we've not tried yet (# of sel hosts < max) - unique=True
- # or failed to select from selected_hosts - unique=False
- # select from all hosts
- (host, qos_cap) = self.host_selection_algorithm(request_spec,
- all_hosts,
- selected_hosts,
- unique)
- if host is None:
- raise exception.NoValidHost(reason="")
-
- return (host, qos_cap)
-
- def _provision_volume(self, context, vol, vsa_id, availability_zone):
-
- if availability_zone is None:
- availability_zone = FLAGS.storage_availability_zone
-
- now = utils.utcnow()
- options = {
- 'size': vol['size'],
- 'user_id': context.user_id,
- 'project_id': context.project_id,
- 'snapshot_id': None,
- 'availability_zone': availability_zone,
- 'status': "creating",
- 'attach_status': "detached",
- 'display_name': vol['name'],
- 'display_description': vol['description'],
- 'volume_type_id': vol['volume_type_id'],
- 'metadata': dict(to_vsa_id=vsa_id),
- }
-
- size = vol['size']
- host = vol['host']
- name = vol['name']
- LOG.debug(_("Provision volume %(name)s of size %(size)s GB on "
- "host %(host)s"), locals())
-
- volume_ref = db.volume_create(context.elevated(), options)
- driver.cast_to_volume_host(context, vol['host'],
- 'create_volume', volume_id=volume_ref['id'],
- snapshot_id=None)
-
- def _check_host_enforcement(self, context, availability_zone):
- if (availability_zone
- and ':' in availability_zone
- and context.is_admin):
- zone, _x, host = availability_zone.partition(':')
- service = db.service_get_by_args(context.elevated(), host,
- 'nova-volume')
- if service['disabled'] or not utils.service_is_up(service):
- raise exception.WillNotSchedule(host=host)
-
- return host
- else:
- return None
-
- def _assign_hosts_to_volumes(self, context, volume_params, forced_host):
-
- prev_volume_type_id = None
- request_spec = {}
- selected_hosts = []
-
- LOG.debug(_("volume_params %(volume_params)s") % locals())
-
- i = 1
- for vol in volume_params:
- name = vol['name']
- LOG.debug(_("%(i)d: Volume %(name)s"), locals())
- i += 1
-
- if forced_host:
- vol['host'] = forced_host
- vol['capabilities'] = None
- continue
-
- volume_type_id = vol['volume_type_id']
- request_spec['size'] = vol['size']
-
- if (prev_volume_type_id is None or
- prev_volume_type_id != volume_type_id):
- # generate list of hosts for this drive type
-
- volume_type = volume_types.get_volume_type(context,
- volume_type_id)
- drive_type = {
- 'name': volume_type['extra_specs'].get('drive_name'),
- 'type': volume_type['extra_specs'].get('drive_type'),
- 'size': int(volume_type['extra_specs'].get('drive_size')),
- 'rpm': volume_type['extra_specs'].get('drive_rpm'),
- }
- request_spec['drive_type'] = drive_type
-
- all_hosts = self._filter_hosts("volume", request_spec)
- prev_volume_type_id = volume_type_id
-
- (host, qos_cap) = self._select_hosts(request_spec,
- all_hosts, selected_hosts)
- vol['host'] = host
- vol['capabilities'] = qos_cap
- self._consume_resource(qos_cap, vol['size'], -1)
-
- def schedule_create_volumes(self, context, request_spec,
- availability_zone=None, *_args, **_kwargs):
- """Picks hosts for hosting multiple volumes."""
- num_volumes = request_spec.get('num_volumes')
- LOG.debug(_("Attempting to spawn %(num_volumes)d volume(s)") %
- locals())
-
- vsa_id = request_spec.get('vsa_id')
- volume_params = request_spec.get('volumes')
-
- host = self._check_host_enforcement(context, availability_zone)
-
- try:
- self._print_capabilities_info()
-
- self._assign_hosts_to_volumes(context, volume_params, host)
-
- for vol in volume_params:
- self._provision_volume(context, vol, vsa_id, availability_zone)
- except Exception:
- LOG.exception(_("Error creating volumes"))
- if vsa_id:
- db.vsa_update(context, vsa_id,
- dict(status=vsa_api.VsaState.FAILED))
-
- for vol in volume_params:
- if 'capabilities' in vol:
- self._consume_resource(vol['capabilities'],
- vol['size'], 1)
- raise
-
- return None
-
- def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
- """Picks the best host based on requested drive type capability."""
- volume_ref = db.volume_get(context, volume_id)
-
- host = self._check_host_enforcement(context,
- volume_ref['availability_zone'])
- if host:
- driver.cast_to_volume_host(context, host, 'create_volume',
- volume_id=volume_id, **_kwargs)
- return None
-
- volume_type_id = volume_ref['volume_type_id']
- if volume_type_id:
- volume_type = volume_types.get_volume_type(context, volume_type_id)
-
- if (volume_type_id is None or
- volume_types.is_vsa_volume(volume_type_id, volume_type)):
-
- LOG.debug(_("Non-VSA volume %d"), volume_ref['id'])
- return super(VsaScheduler, self).schedule_create_volume(context,
- volume_id, *_args, **_kwargs)
-
- self._print_capabilities_info()
-
- drive_type = {
- 'name': volume_type['extra_specs'].get('drive_name'),
- 'type': volume_type['extra_specs'].get('drive_type'),
- 'size': int(volume_type['extra_specs'].get('drive_size')),
- 'rpm': volume_type['extra_specs'].get('drive_rpm'),
- }
-
- LOG.debug(_("Spawning volume %(volume_id)s with drive type "
- "%(drive_type)s"), locals())
-
- request_spec = {'size': volume_ref['size'],
- 'drive_type': drive_type}
- hosts = self._filter_hosts("volume", request_spec)
-
- try:
- (host, qos_cap) = self._select_hosts(request_spec, all_hosts=hosts)
- except Exception:
- LOG.exception(_("Error creating volume"))
- if volume_ref['to_vsa_id']:
- db.vsa_update(context, volume_ref['to_vsa_id'],
- dict(status=vsa_api.VsaState.FAILED))
- raise
-
- if host:
- driver.cast_to_volume_host(context, host, 'create_volume',
- volume_id=volume_id, **_kwargs)
-
- def _consume_full_drive(self, qos_values, direction):
- qos_values['FullDrive']['NumFreeDrives'] += direction
- qos_values['FullDrive']['NumOccupiedDrives'] -= direction
-
- def _consume_partition(self, qos_values, size, direction):
-
- if qos_values['PartitionDrive']['PartitionSize'] != 0:
- partition_size = qos_values['PartitionDrive']['PartitionSize']
- else:
- partition_size = size
- part_per_drive = qos_values['DriveCapacity'] / partition_size
-
- if (direction == -1 and
- qos_values['PartitionDrive']['NumFreePartitions'] == 0):
-
- self._consume_full_drive(qos_values, direction)
- qos_values['PartitionDrive']['NumFreePartitions'] += part_per_drive
-
- qos_values['PartitionDrive']['NumFreePartitions'] += direction
- qos_values['PartitionDrive']['NumOccupiedPartitions'] -= direction
-
- if (direction == 1 and
- qos_values['PartitionDrive']['NumFreePartitions'] >=
- part_per_drive):
-
- self._consume_full_drive(qos_values, direction)
- qos_values['PartitionDrive']['NumFreePartitions'] -= part_per_drive
-
- def _consume_resource(self, qos_values, size, direction):
- if qos_values is None:
- LOG.debug(_("No capability selected for volume of size %(size)s"),
- locals())
- return
-
- if size == 0: # full drive match
- qos_values['AvailableCapacity'] += (direction *
- qos_values['DriveCapacity'])
- self._consume_full_drive(qos_values, direction)
- else:
- qos_values['AvailableCapacity'] += direction * GB_TO_BYTES(size)
- self._consume_partition(qos_values, GB_TO_BYTES(size), direction)
- return
-
- def _print_capabilities_info(self):
- host_list = self._get_service_states().iteritems()
- for host, host_dict in host_list:
- for service_name, service_dict in host_dict.iteritems():
- if service_name != "volume":
- continue
-
- LOG.info(_("Host %s:"), host)
-
- gos_info = service_dict.get('drive_qos_info', {})
- for qosgrp, qos_values in gos_info.iteritems():
- total = qos_values['TotalDrives']
- used = qos_values['FullDrive']['NumOccupiedDrives']
- free = qos_values['FullDrive']['NumFreeDrives']
- avail = BYTES_TO_GB(qos_values['AvailableCapacity'])
-
- LOG.info(_("\tDrive %(qosgrp)-25s: total %(total)2s, "
- "used %(used)2s, free %(free)2s. Available "
- "capacity %(avail)-5s"), locals())
-
-
-class VsaSchedulerLeastUsedHost(VsaScheduler):
- """
- Implements VSA scheduler to select the host with least used capacity
- of particular type.
- """
-
- def __init__(self, *args, **kwargs):
- super(VsaSchedulerLeastUsedHost, self).__init__(*args, **kwargs)
-
- def host_selection_algorithm(self, request_spec, all_hosts,
- selected_hosts, unique):
- size = request_spec['size']
- drive_type = request_spec['drive_type']
- best_host = None
- best_qoscap = None
- best_cap = None
- min_used = 0
-
- for (host, capabilities) in all_hosts:
-
- has_enough_capacity = False
- used_capacity = 0
- for qosgrp, qos_values in capabilities.iteritems():
-
- used_capacity = (used_capacity + qos_values['TotalCapacity'] -
- qos_values['AvailableCapacity'])
-
- if self._qosgrp_match(drive_type, qos_values):
- # we found required qosgroup
-
- if size == 0: # full drive match
- if qos_values['FullDrive']['NumFreeDrives'] > 0:
- has_enough_capacity = True
- matched_qos = qos_values
- else:
- break
- else:
- _fp = qos_values['PartitionDrive']['NumFreePartitions']
- _fd = qos_values['FullDrive']['NumFreeDrives']
- if (qos_values['AvailableCapacity'] >= size and
- (_fp > 0 or _fd > 0)):
- has_enough_capacity = True
- matched_qos = qos_values
- else:
- break
-
- if (has_enough_capacity and
- self._allowed_to_use_host(host, selected_hosts, unique) and
- (best_host is None or used_capacity < min_used)):
-
- min_used = used_capacity
- best_host = host
- best_qoscap = matched_qos
- best_cap = capabilities
-
- if best_host:
- self._add_hostcap_to_list(selected_hosts, best_host, best_cap)
- min_used = BYTES_TO_GB(min_used)
- LOG.debug(_("\t LeastUsedHost: Best host: %(best_host)s. "
- "(used capacity %(min_used)s)"), locals())
- return (best_host, best_qoscap)
-
-
-class VsaSchedulerMostAvailCapacity(VsaScheduler):
- """
- Implements VSA scheduler to select the host with most available capacity
- of one particular type.
- """
-
- def __init__(self, *args, **kwargs):
- super(VsaSchedulerMostAvailCapacity, self).__init__(*args, **kwargs)
-
- def host_selection_algorithm(self, request_spec, all_hosts,
- selected_hosts, unique):
- size = request_spec['size']
- drive_type = request_spec['drive_type']
- best_host = None
- best_qoscap = None
- best_cap = None
- max_avail = 0
-
- for (host, capabilities) in all_hosts:
- for qosgrp, qos_values in capabilities.iteritems():
- if self._qosgrp_match(drive_type, qos_values):
- # we found required qosgroup
-
- if size == 0: # full drive match
- available = qos_values['FullDrive']['NumFreeDrives']
- else:
- available = qos_values['AvailableCapacity']
-
- if (available > max_avail and
- self._allowed_to_use_host(host, selected_hosts,
- unique)):
- max_avail = available
- best_host = host
- best_qoscap = qos_values
- best_cap = capabilities
- break # go to the next host
-
- if best_host:
- self._add_hostcap_to_list(selected_hosts, best_host, best_cap)
- type_str = "drives" if size == 0 else "bytes"
- LOG.debug(_("\t MostAvailCap: Best host: %(best_host)s. "
- "(available %(max_avail)s %(type_str)s)"), locals())
-
- return (best_host, best_qoscap)
diff --git a/nova/tests/api/openstack/compute/contrib/test_vsa.py b/nova/tests/api/openstack/compute/contrib/test_vsa.py
deleted file mode 100644
index 806bd8b2e..000000000
--- a/nova/tests/api/openstack/compute/contrib/test_vsa.py
+++ /dev/null
@@ -1,637 +0,0 @@
-# Copyright 2011 OpenStack LLC.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import datetime
-import json
-
-from lxml import etree
-import webob
-
-from nova.api.openstack.compute.contrib import virtual_storage_arrays as \
- vsa_ext
-import nova.db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova.tests.api.openstack import fakes
-from nova.volume import api as volume_api
-from nova.vsa import api as vsa_api
-
-
-FLAGS = flags.FLAGS
-
-LOG = logging.getLogger(__name__)
-
-last_param = {}
-
-
-def _get_default_vsa_param():
- return {
- 'display_name': 'Test_VSA_name',
- 'display_description': 'Test_VSA_description',
- 'vc_count': 1,
- 'instance_type': 'm1.small',
- 'instance_type_id': 5,
- 'image_name': None,
- 'availability_zone': None,
- 'storage': [],
- 'shared': False,
- }
-
-
-def stub_vsa_create(self, context, **param):
- global last_param
- LOG.debug(_("_create: param=%s"), param)
- param['id'] = 123
- param['name'] = 'Test name'
- param['instance_type_id'] = 5
- last_param = param
- return param
-
-
-def stub_vsa_delete(self, context, vsa_id):
- global last_param
- last_param = dict(vsa_id=vsa_id)
-
- LOG.debug(_("_delete: %s"), locals())
- if vsa_id != '123':
- raise exception.NotFound
-
-
-def stub_vsa_get(self, context, vsa_id):
- global last_param
- last_param = dict(vsa_id=vsa_id)
-
- LOG.debug(_("_get: %s"), locals())
- if vsa_id != '123':
- raise exception.NotFound
-
- param = _get_default_vsa_param()
- param['id'] = vsa_id
- return param
-
-
-def stub_vsa_get_all(self, context):
- LOG.debug(_("_get_all: %s"), locals())
- param = _get_default_vsa_param()
- param['id'] = 123
- return [param]
-
-
-class VSAApiTest(test.TestCase):
- def setUp(self):
- super(VSAApiTest, self).setUp()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_networking(self.stubs)
- fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
- self.stubs.Set(vsa_api.API, "create", stub_vsa_create)
- self.stubs.Set(vsa_api.API, "delete", stub_vsa_delete)
- self.stubs.Set(vsa_api.API, "get", stub_vsa_get)
- self.stubs.Set(vsa_api.API, "get_all", stub_vsa_get_all)
-
- def test_vsa_create(self):
- global last_param
- last_param = {}
-
- vsa = {"displayName": "VSA Test Name",
- "displayDescription": "VSA Test Desc"}
- body = dict(vsa=vsa)
- req = webob.Request.blank('/v2/fake/zadr-vsa')
- req.method = 'POST'
- req.body = json.dumps(body)
- req.headers['content-type'] = 'application/json'
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- # Compare if parameters were correctly passed to stub
- self.assertEqual(last_param['display_name'], "VSA Test Name")
- self.assertEqual(last_param['display_description'], "VSA Test Desc")
-
- resp_dict = json.loads(resp.body)
- self.assertTrue('vsa' in resp_dict)
- self.assertEqual(resp_dict['vsa']['displayName'], vsa['displayName'])
- self.assertEqual(resp_dict['vsa']['displayDescription'],
- vsa['displayDescription'])
-
- def test_vsa_create_no_body(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa')
- req.method = 'POST'
- req.body = json.dumps({})
- req.headers['content-type'] = 'application/json'
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 422)
-
- def test_vsa_delete(self):
- global last_param
- last_param = {}
-
- vsa_id = 123
- req = webob.Request.blank('/v2/fake/zadr-vsa/%d' % vsa_id)
- req.method = 'DELETE'
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
- self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
-
- def test_vsa_delete_invalid_id(self):
- global last_param
- last_param = {}
-
- vsa_id = 234
- req = webob.Request.blank('/v2/fake/zadr-vsa/%d' % vsa_id)
- req.method = 'DELETE'
-
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 404)
- self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
-
- def test_vsa_show(self):
- global last_param
- last_param = {}
-
- vsa_id = 123
- req = webob.Request.blank('/v2/fake/zadr-vsa/%d' % vsa_id)
- req.method = 'GET'
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
- self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
-
- resp_dict = json.loads(resp.body)
- self.assertTrue('vsa' in resp_dict)
- self.assertEqual(resp_dict['vsa']['id'], str(vsa_id))
-
- def test_vsa_show_invalid_id(self):
- global last_param
- last_param = {}
-
- vsa_id = 234
- req = webob.Request.blank('/v2/fake/zadr-vsa/%d' % vsa_id)
- req.method = 'GET'
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 404)
- self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
-
- def test_vsa_index(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa')
- req.method = 'GET'
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- resp_dict = json.loads(resp.body)
-
- self.assertTrue('vsaSet' in resp_dict)
- resp_vsas = resp_dict['vsaSet']
- self.assertEqual(len(resp_vsas), 1)
-
- resp_vsa = resp_vsas.pop()
- self.assertEqual(resp_vsa['id'], 123)
-
- def test_vsa_detail(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/detail')
- req.method = 'GET'
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- resp_dict = json.loads(resp.body)
-
- self.assertTrue('vsaSet' in resp_dict)
- resp_vsas = resp_dict['vsaSet']
- self.assertEqual(len(resp_vsas), 1)
-
- resp_vsa = resp_vsas.pop()
- self.assertEqual(resp_vsa['id'], 123)
-
-
-def stub_get_vsa_volume_type(self, context):
- return {'id': 1,
- 'name': 'VSA volume type',
- 'extra_specs': {'type': 'vsa_volume'}}
-
-
-def return_vsa(context, vsa_id):
- return {'id': vsa_id}
-
-
-class VSAVolumeApiTest(test.TestCase):
-
- def setUp(self, test_obj=None, test_objs=None):
- super(VSAVolumeApiTest, self).setUp()
- fakes.FakeAuthManager.reset_fake_data()
- fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_networking(self.stubs)
- fakes.stub_out_rate_limiting(self.stubs)
- fakes.stub_out_auth(self.stubs)
- self.stubs.Set(nova.db, 'vsa_get', return_vsa)
- self.stubs.Set(vsa_api.API, "get_vsa_volume_type",
- stub_get_vsa_volume_type)
-
- self.stubs.Set(volume_api.API, "update", fakes.stub_volume_update)
- self.stubs.Set(volume_api.API, "delete", fakes.stub_volume_delete)
- self.stubs.Set(volume_api.API, "get", fakes.stub_volume_get)
- self.stubs.Set(volume_api.API, "get_all", fakes.stub_volume_get_all)
-
- self.test_obj = test_obj if test_obj else "volume"
- self.test_objs = test_objs if test_objs else "volumes"
-
- def test_vsa_volume_create(self):
- self.stubs.Set(volume_api.API, "create", fakes.stub_volume_create)
-
- vol = {"size": 100,
- "displayName": "VSA Volume Test Name",
- "displayDescription": "VSA Volume Test Desc"}
- body = {self.test_obj: vol}
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s' % self.test_objs)
- req.method = 'POST'
- req.body = json.dumps(body)
- req.headers['content-type'] = 'application/json'
- resp = req.get_response(fakes.wsgi_app())
-
- if self.test_obj == "volume":
- self.assertEqual(resp.status_int, 200)
-
- resp_dict = json.loads(resp.body)
- self.assertTrue(self.test_obj in resp_dict)
- self.assertEqual(resp_dict[self.test_obj]['size'],
- vol['size'])
- self.assertEqual(resp_dict[self.test_obj]['displayName'],
- vol['displayName'])
- self.assertEqual(resp_dict[self.test_obj]['displayDescription'],
- vol['displayDescription'])
- else:
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_create_no_body(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s' % self.test_objs)
- req.method = 'POST'
- req.body = json.dumps({})
- req.headers['content-type'] = 'application/json'
-
- resp = req.get_response(fakes.wsgi_app())
- if self.test_obj == "volume":
- self.assertEqual(resp.status_int, 422)
- else:
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_index(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s' % self.test_objs)
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- def test_vsa_volume_detail(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/detail' %
- self.test_objs)
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- def test_vsa_volume_show(self):
- obj_num = 234 if self.test_objs == "volumes" else 345
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/%s' %
- (self.test_objs, obj_num))
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 200)
-
- def test_vsa_volume_show_no_vsa_assignment(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/4/%s/333' %
- self.test_objs)
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_show_no_volume(self):
- self.stubs.Set(volume_api.API, "get", fakes.stub_volume_get_notfound)
-
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/333' %
- self.test_objs)
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 404)
-
- def test_vsa_volume_update(self):
- obj_num = 234 if self.test_objs == "volumes" else 345
- update = {"status": "available",
- "displayName": "Test Display name"}
- body = {self.test_obj: update}
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/%s' %
- (self.test_objs, obj_num))
- req.method = 'PUT'
- req.body = json.dumps(body)
- req.headers['content-type'] = 'application/json'
-
- resp = req.get_response(fakes.wsgi_app())
- if self.test_obj == "volume":
- self.assertEqual(resp.status_int, 202)
- else:
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_delete(self):
- obj_num = 234 if self.test_objs == "volumes" else 345
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/%s' %
- (self.test_objs, obj_num))
- req.method = 'DELETE'
- resp = req.get_response(fakes.wsgi_app())
- if self.test_obj == "volume":
- self.assertEqual(resp.status_int, 202)
- else:
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_delete_no_vsa_assignment(self):
- req = webob.Request.blank('/v2/fake/zadr-vsa/4/%s/333' %
- self.test_objs)
- req.method = 'DELETE'
- resp = req.get_response(fakes.wsgi_app())
- self.assertEqual(resp.status_int, 400)
-
- def test_vsa_volume_delete_no_volume(self):
- self.stubs.Set(volume_api.API, "get", fakes.stub_volume_get_notfound)
-
- req = webob.Request.blank('/v2/fake/zadr-vsa/123/%s/333' %
- self.test_objs)
- req.method = 'DELETE'
- resp = req.get_response(fakes.wsgi_app())
- if self.test_obj == "volume":
- self.assertEqual(resp.status_int, 404)
- else:
- self.assertEqual(resp.status_int, 400)
-
-
-class VSADriveApiTest(VSAVolumeApiTest):
- def setUp(self):
- super(VSADriveApiTest, self).setUp(test_obj="drive",
- test_objs="drives")
-
-
-class SerializerTestCommon(test.TestCase):
- def _verify_attrs(self, obj, tree, attrs):
- for attr in attrs:
- self.assertEqual(str(obj[attr]), tree.get(attr))
-
-
-class VsaSerializerTest(SerializerTestCommon):
- def test_serialize_show_create(self):
- serializer = vsa_ext.VsaTemplate()
- exemplar = dict(
- id='vsa_id',
- name='vsa_name',
- displayName='vsa_display_name',
- displayDescription='vsa_display_desc',
- createTime=datetime.datetime.now(),
- status='active',
- vcType='vsa_instance_type',
- vcCount=24,
- driveCount=48,
- ipAddress='10.11.12.13')
- text = serializer.serialize(dict(vsa=exemplar))
-
- print text
- tree = etree.fromstring(text)
-
- self.assertEqual('vsa', tree.tag)
- self._verify_attrs(exemplar, tree, exemplar.keys())
-
- def test_serialize_index_detail(self):
- serializer = vsa_ext.VsaSetTemplate()
- exemplar = [dict(
- id='vsa1_id',
- name='vsa1_name',
- displayName='vsa1_display_name',
- displayDescription='vsa1_display_desc',
- createTime=datetime.datetime.now(),
- status='active',
- vcType='vsa1_instance_type',
- vcCount=24,
- driveCount=48,
- ipAddress='10.11.12.13'),
- dict(
- id='vsa2_id',
- name='vsa2_name',
- displayName='vsa2_display_name',
- displayDescription='vsa2_display_desc',
- createTime=datetime.datetime.now(),
- status='active',
- vcType='vsa2_instance_type',
- vcCount=42,
- driveCount=84,
- ipAddress='11.12.13.14')]
- text = serializer.serialize(dict(vsaSet=exemplar))
-
- print text
- tree = etree.fromstring(text)
-
- self.assertEqual('vsaSet', tree.tag)
- self.assertEqual(len(exemplar), len(tree))
- for idx, child in enumerate(tree):
- self.assertEqual('vsa', child.tag)
- self._verify_attrs(exemplar[idx], child, exemplar[idx].keys())
-
-
-class VsaVolumeSerializerTest(SerializerTestCommon):
- show_serializer = vsa_ext.VsaVolumeTemplate
- index_serializer = vsa_ext.VsaVolumesTemplate
- object = 'volume'
- objects = 'volumes'
-
- def _verify_voldrive(self, vol, tree):
- self.assertEqual(self.object, tree.tag)
-
- self._verify_attrs(vol, tree, ('id', 'status', 'size',
- 'availabilityZone', 'createdAt',
- 'displayName', 'displayDescription',
- 'volumeType', 'vsaId', 'name'))
-
- for child in tree:
- self.assertTrue(child.tag in ('attachments', 'metadata'))
- if child.tag == 'attachments':
- self.assertEqual(1, len(child))
- self.assertEqual('attachment', child[0].tag)
- self._verify_attrs(vol['attachments'][0], child[0],
- ('id', 'volumeId', 'serverId', 'device'))
- elif child.tag == 'metadata':
- not_seen = set(vol['metadata'].keys())
- for gr_child in child:
- self.assertTrue(gr_child.tag in not_seen)
- self.assertEqual(str(vol['metadata'][gr_child.tag]),
- gr_child.text)
- not_seen.remove(gr_child.tag)
- self.assertEqual(0, len(not_seen))
-
- def test_show_create_serializer(self):
- serializer = self.show_serializer()
- raw_volume = dict(
- id='vol_id',
- status='vol_status',
- size=1024,
- availabilityZone='vol_availability',
- createdAt=datetime.datetime.now(),
- attachments=[dict(
- id='vol_id',
- volumeId='vol_id',
- serverId='instance_uuid',
- device='/foo')],
- displayName='vol_name',
- displayDescription='vol_desc',
- volumeType='vol_type',
- metadata=dict(
- foo='bar',
- baz='quux',
- ),
- vsaId='vol_vsa_id',
- name='vol_vsa_name',
- )
- text = serializer.serialize({self.object: raw_volume})
-
- print text
- tree = etree.fromstring(text)
-
- self._verify_voldrive(raw_volume, tree)
-
- def test_index_detail_serializer(self):
- serializer = self.index_serializer()
- raw_volumes = [dict(
- id='vol1_id',
- status='vol1_status',
- size=1024,
- availabilityZone='vol1_availability',
- createdAt=datetime.datetime.now(),
- attachments=[dict(
- id='vol1_id',
- volumeId='vol1_id',
- serverId='instance_uuid',
- device='/foo1')],
- displayName='vol1_name',
- displayDescription='vol1_desc',
- volumeType='vol1_type',
- metadata=dict(
- foo='vol1_foo',
- bar='vol1_bar',
- ),
- vsaId='vol1_vsa_id',
- name='vol1_vsa_name',
- ),
- dict(
- id='vol2_id',
- status='vol2_status',
- size=1024,
- availabilityZone='vol2_availability',
- createdAt=datetime.datetime.now(),
- attachments=[dict(
- id='vol2_id',
- volumeId='vol2_id',
- serverId='instance_uuid',
- device='/foo2')],
- displayName='vol2_name',
- displayDescription='vol2_desc',
- volumeType='vol2_type',
- metadata=dict(
- foo='vol2_foo',
- bar='vol2_bar',
- ),
- vsaId='vol2_vsa_id',
- name='vol2_vsa_name',
- )]
- text = serializer.serialize({self.objects: raw_volumes})
-
- print text
- tree = etree.fromstring(text)
-
- self.assertEqual(self.objects, tree.tag)
- self.assertEqual(len(raw_volumes), len(tree))
- for idx, child in enumerate(tree):
- self._verify_voldrive(raw_volumes[idx], child)
-
-
-class VsaDriveSerializerTest(VsaVolumeSerializerTest):
- show_serializer = vsa_ext.VsaDriveTemplate
- index_serializer = vsa_ext.VsaDrivesTemplate
- object = 'drive'
- objects = 'drives'
-
-
-class VsaVPoolSerializerTest(SerializerTestCommon):
- def _verify_vpool(self, vpool, tree):
- self._verify_attrs(vpool, tree, ('id', 'vsaId', 'name', 'displayName',
- 'displayDescription', 'driveCount',
- 'protection', 'stripeSize',
- 'stripeWidth', 'createTime',
- 'status'))
-
- self.assertEqual(1, len(tree))
- self.assertEqual('driveIds', tree[0].tag)
- self.assertEqual(len(vpool['driveIds']), len(tree[0]))
- for idx, gr_child in enumerate(tree[0]):
- self.assertEqual('driveId', gr_child.tag)
- self.assertEqual(str(vpool['driveIds'][idx]), gr_child.text)
-
- def test_vpool_create_show_serializer(self):
- serializer = vsa_ext.VsaVPoolTemplate()
- exemplar = dict(
- id='vpool_id',
- vsaId='vpool_vsa_id',
- name='vpool_vsa_name',
- displayName='vpool_display_name',
- displayDescription='vpool_display_desc',
- driveCount=24,
- driveIds=['drive1', 'drive2', 'drive3'],
- protection='protected',
- stripeSize=1024,
- stripeWidth=2048,
- createTime=datetime.datetime.now(),
- status='available')
- text = serializer.serialize(dict(vpool=exemplar))
-
- print text
- tree = etree.fromstring(text)
-
- self._verify_vpool(exemplar, tree)
-
- def test_vpool_index_serializer(self):
- serializer = vsa_ext.VsaVPoolsTemplate()
- exemplar = [dict(
- id='vpool1_id',
- vsaId='vpool1_vsa_id',
- name='vpool1_vsa_name',
- displayName='vpool1_display_name',
- displayDescription='vpool1_display_desc',
- driveCount=24,
- driveIds=['drive1', 'drive2', 'drive3'],
- protection='protected',
- stripeSize=1024,
- stripeWidth=2048,
- createTime=datetime.datetime.now(),
- status='available'),
- dict(
- id='vpool2_id',
- vsaId='vpool2_vsa_id',
- name='vpool2_vsa_name',
- displayName='vpool2_display_name',
- displayDescription='vpool2_display_desc',
- driveCount=42,
- driveIds=['drive4', 'drive5', 'drive6'],
- protection='protected',
- stripeSize=512,
- stripeWidth=256,
- createTime=datetime.datetime.now(),
- status='available')]
- text = serializer.serialize(dict(vpools=exemplar))
-
- print text
- tree = etree.fromstring(text)
-
- self.assertEqual('vpools', tree.tag)
- self.assertEqual(len(exemplar), len(tree))
- for idx, child in enumerate(tree):
- self._verify_vpool(exemplar[idx], child)
diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py
index 88abf0b35..62136fd5d 100644
--- a/nova/tests/api/openstack/compute/test_extensions.py
+++ b/nova/tests/api/openstack/compute/test_extensions.py
@@ -182,7 +182,6 @@ class ExtensionControllerTest(ExtensionTestCase):
"ServerStartStop",
"SimpleTenantUsage",
"Users",
- "VSAs",
"VirtualInterfaces",
"Volumes",
"VolumeTypes",
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 80f72aaea..af79df9cd 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -645,14 +645,7 @@ def stub_volume_delete(self, context, *args, **param):
def stub_volume_get(self, context, volume_id):
- vol = stub_volume(volume_id)
- if volume_id == '234':
- meta = {'key': 'from_vsa_id', 'value': '123'}
- vol['volume_metadata'].append(meta)
- if volume_id == '345':
- meta = {'key': 'to_vsa_id', 'value': '123'}
- vol['volume_metadata'].append(meta)
- return vol
+ return stub_volume(volume_id)
def stub_volume_get_notfound(self, context, volume_id):
diff --git a/nova/tests/scheduler/test_vsa_scheduler.py b/nova/tests/scheduler/test_vsa_scheduler.py
deleted file mode 100644
index 853ac9835..000000000
--- a/nova/tests/scheduler/test_vsa_scheduler.py
+++ /dev/null
@@ -1,626 +0,0 @@
-# Copyright 2011 OpenStack LLC.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import rpc
-from nova.scheduler import vsa as vsa_sched
-from nova.tests.scheduler import test_scheduler
-from nova import utils
-from nova.volume import volume_types
-
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-
-scheduled_volumes = []
-scheduled_volume = {}
-global_volume = {}
-
-
-def fake_rpc_cast(*args, **kwargs):
- pass
-
-
-class FakeVsaLeastUsedScheduler(
- vsa_sched.VsaSchedulerLeastUsedHost):
- # No need to stub anything at the moment
- pass
-
-
-class FakeVsaMostAvailCapacityScheduler(
- vsa_sched.VsaSchedulerMostAvailCapacity):
- # No need to stub anything at the moment
- pass
-
-
-class VsaSchedulerTestCase(test_scheduler.SchedulerTestCase):
-
- driver_cls = FakeVsaLeastUsedScheduler
-
- def setUp(self):
- super(VsaSchedulerTestCase, self).setUp()
-
- self.host_num = 10
- self.drive_type_num = 5
-
- self.stubs.Set(rpc, 'cast', fake_rpc_cast)
- self.stubs.Set(self.driver,
- '_get_service_states', self._fake_get_service_states)
- self.stubs.Set(self.driver,
- '_provision_volume', self._fake_provision_volume)
- self.stubs.Set(db, 'vsa_update', self._fake_vsa_update)
-
- self.stubs.Set(db, 'volume_get', self._fake_volume_get)
- self.stubs.Set(db, 'volume_update', self._fake_volume_update)
-
- self.created_types_lst = []
-
- def tearDown(self):
- for name in self.created_types_lst:
- volume_types.destroy(self.context.elevated(), name)
- super(VsaSchedulerTestCase, self).tearDown()
-
- def _get_vol_creation_request(self, num_vols, drive_ix, size=0):
- volume_params = []
- for i in range(num_vols):
-
- name = 'name_' + str(i)
- try:
- volume_types.create(self.context.elevated(), name,
- extra_specs={'type': 'vsa_drive',
- 'drive_name': name,
- 'drive_type': 'type_' + str(drive_ix),
- 'drive_size': 1 + 100 * (drive_ix)})
- self.created_types_lst.append(name)
- except exception.VolumeTypeExists:
- # type is already created
- pass
-
- volume_type = volume_types.get_volume_type_by_name(self.context,
- name)
- volume = {'size': size,
- 'snapshot_id': None,
- 'name': 'vol_' + str(i),
- 'description': None,
- 'volume_type_id': volume_type['id']}
- volume_params.append(volume)
-
- return {'num_volumes': len(volume_params),
- 'vsa_id': 123,
- 'volumes': volume_params}
-
- def _generate_default_service_states(self):
- service_states = {}
- for i in range(self.host_num):
- host = {}
- hostname = 'host_' + str(i)
- if hostname in self.exclude_host_list:
- continue
-
- host['volume'] = {'timestamp': utils.utcnow(),
- 'drive_qos_info': {}}
-
- for j in range(self.drive_type_start_ix,
- self.drive_type_start_ix + self.drive_type_num):
- dtype = {}
- dtype['Name'] = 'name_' + str(j)
- dtype['DriveType'] = 'type_' + str(j)
- dtype['TotalDrives'] = 2 * (self.init_num_drives + i)
- dtype['DriveCapacity'] = vsa_sched.GB_TO_BYTES(1 + 100 * j)
- dtype['TotalCapacity'] = (dtype['TotalDrives'] *
- dtype['DriveCapacity'])
- dtype['AvailableCapacity'] = ((dtype['TotalDrives'] - i) *
- dtype['DriveCapacity'])
- dtype['DriveRpm'] = 7200
- dtype['DifCapable'] = 0
- dtype['SedCapable'] = 0
- dtype['PartitionDrive'] = {
- 'PartitionSize': 0,
- 'NumOccupiedPartitions': 0,
- 'NumFreePartitions': 0}
- dtype['FullDrive'] = {
- 'NumFreeDrives': dtype['TotalDrives'] - i,
- 'NumOccupiedDrives': i}
- host['volume']['drive_qos_info'][dtype['Name']] = dtype
-
- service_states[hostname] = host
-
- return service_states
-
- def _print_service_states(self):
- for host, host_val in self.service_states.iteritems():
- LOG.info(_("Host %s"), host)
- total_used = 0
- total_available = 0
- qos = host_val['volume']['drive_qos_info']
-
- for k, d in qos.iteritems():
- LOG.info("\t%s: type %s: drives (used %2d, total %2d) "
- "size %3d, total %4d, used %4d, avail %d",
- k,
- d['DriveType'],
- d['FullDrive']['NumOccupiedDrives'],
- d['TotalDrives'],
- vsa_sched.BYTES_TO_GB(d['DriveCapacity']),
- vsa_sched.BYTES_TO_GB(d['TotalCapacity']),
- vsa_sched.BYTES_TO_GB(d['TotalCapacity'] -
- d['AvailableCapacity']),
- vsa_sched.BYTES_TO_GB(d['AvailableCapacity']))
-
- total_used += vsa_sched.BYTES_TO_GB(d['TotalCapacity'] -
- d['AvailableCapacity'])
- total_available += vsa_sched.BYTES_TO_GB(
- d['AvailableCapacity'])
- LOG.info("Host %s: used %d, avail %d",
- host, total_used, total_available)
-
- def _set_service_states(self, host_num,
- drive_type_start_ix, drive_type_num,
- init_num_drives=10,
- exclude_host_list=[]):
- self.host_num = host_num
- self.drive_type_start_ix = drive_type_start_ix
- self.drive_type_num = drive_type_num
- self.exclude_host_list = exclude_host_list
- self.init_num_drives = init_num_drives
- self.service_states = self._generate_default_service_states()
-
- def _get_service_states(self):
- return self.service_states
-
- def _fake_get_service_states(self):
- return self._get_service_states()
-
- def _fake_provision_volume(self, context, vol, vsa_id, availability_zone):
- global scheduled_volumes
- scheduled_volumes.append(dict(vol=vol,
- vsa_id=vsa_id,
- az=availability_zone))
- name = vol['name']
- host = vol['host']
- LOG.debug(_("Test: provision vol %(name)s on host %(host)s"),
- locals())
- LOG.debug(_("\t vol=%(vol)s"), locals())
-
- def _fake_vsa_update(self, context, vsa_id, values):
- LOG.debug(_("Test: VSA update request: vsa_id=%(vsa_id)s "
- "values=%(values)s"), locals())
-
- def _fake_volume_create(self, context, options):
- LOG.debug(_("Test: Volume create: %s"), options)
- options['id'] = 123
- global global_volume
- global_volume = options
- return options
-
- def _fake_volume_get(self, context, volume_id):
- LOG.debug(_("Test: Volume get request: id=%(volume_id)s"), locals())
- global global_volume
- global_volume['id'] = volume_id
- global_volume['availability_zone'] = None
- return global_volume
-
- def _fake_volume_update(self, context, volume_id, values):
- LOG.debug(_("Test: Volume update request: id=%(volume_id)s "
- "values=%(values)s"), locals())
- global scheduled_volume
- scheduled_volume = {'id': volume_id, 'host': values['host']}
-
- def _fake_service_get_by_args(self, context, host, binary):
- return {'host': 'fake_host', 'disabled': False}
-
- def _fake_service_is_up_True(self, service):
- return True
-
- def _fake_service_is_up_False(self, service):
- return False
-
- def test_vsa_sched_create_volumes_simple(self):
- global scheduled_volumes
- scheduled_volumes = []
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=10,
- exclude_host_list=['host_1', 'host_3'])
- prev = self._generate_default_service_states()
- request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
-
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- self.assertEqual(len(scheduled_volumes), 3)
- self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_0')
- self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_2')
- self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_4')
-
- cur = self._get_service_states()
- for host in ['host_0', 'host_2', 'host_4']:
- cur_dtype = cur[host]['volume']['drive_qos_info']['name_2']
- prev_dtype = prev[host]['volume']['drive_qos_info']['name_2']
- self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
- self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
- prev_dtype['FullDrive']['NumFreeDrives'] - 1)
- self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
- prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
-
- def test_vsa_sched_no_drive_type(self):
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=1)
- request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=6)
- self.assertRaises(exception.NoValidHost,
- self.driver.schedule_create_volumes,
- self.context,
- request_spec,
- availability_zone=None)
-
- def test_vsa_sched_no_enough_drives(self):
- global scheduled_volumes
- scheduled_volumes = []
-
- self._set_service_states(host_num=3,
- drive_type_start_ix=0,
- drive_type_num=1,
- init_num_drives=0)
- prev = self._generate_default_service_states()
- request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=0)
-
- self.assertRaises(exception.NoValidHost,
- self.driver.schedule_create_volumes,
- self.context,
- request_spec,
- availability_zone=None)
-
- # check that everything was returned back
- cur = self._get_service_states()
- for k, v in prev.iteritems():
- self.assertEqual(prev[k]['volume']['drive_qos_info'],
- cur[k]['volume']['drive_qos_info'])
-
- def test_vsa_sched_wrong_topic(self):
- self._set_service_states(host_num=1,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=1)
- states = self._get_service_states()
- new_states = {}
- new_states['host_0'] = {'compute': states['host_0']['volume']}
- self.service_states = new_states
- request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
-
- self.assertRaises(exception.NoValidHost,
- self.driver.schedule_create_volumes,
- self.context,
- request_spec,
- availability_zone=None)
-
- def test_vsa_sched_provision_volume(self):
- global global_volume
- global_volume = {}
- self._set_service_states(host_num=1,
- drive_type_start_ix=0,
- drive_type_num=1,
- init_num_drives=1)
- request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
-
- self.stubs.UnsetAll()
- self.stubs.Set(self.driver,
- '_get_service_states', self._fake_get_service_states)
- self.stubs.Set(db, 'volume_create', self._fake_volume_create)
- self.stubs.Set(db, 'volume_update', self._fake_volume_update)
- self.stubs.Set(rpc, 'cast', fake_rpc_cast)
-
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- self.assertEqual(request_spec['volumes'][0]['name'],
- global_volume['display_name'])
-
- def test_vsa_sched_no_free_drives(self):
- self._set_service_states(host_num=1,
- drive_type_start_ix=0,
- drive_type_num=1,
- init_num_drives=1)
- request_spec = self._get_vol_creation_request(num_vols=1, drive_ix=0)
-
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- cur = self._get_service_states()
- cur_dtype = cur['host_0']['volume']['drive_qos_info']['name_0']
- self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'], 1)
-
- new_request = self._get_vol_creation_request(num_vols=1, drive_ix=0)
-
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
- self._print_service_states()
-
- self.assertRaises(exception.NoValidHost,
- self.driver.schedule_create_volumes,
- self.context,
- new_request,
- availability_zone=None)
-
- def test_vsa_sched_forced_host(self):
- global scheduled_volumes
- scheduled_volumes = []
-
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=10)
-
- request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
-
- self.assertRaises(exception.HostBinaryNotFound,
- self.driver.schedule_create_volumes,
- self.context.elevated(),
- request_spec,
- availability_zone="nova:host_5")
-
- self.stubs.Set(db,
- 'service_get_by_args', self._fake_service_get_by_args)
- self.stubs.Set(utils,
- 'service_is_up', self._fake_service_is_up_False)
-
- self.assertRaises(exception.WillNotSchedule,
- self.driver.schedule_create_volumes,
- self.context.elevated(),
- request_spec,
- availability_zone="nova:host_5")
-
- self.stubs.Set(utils,
- 'service_is_up', self._fake_service_is_up_True)
-
- self.driver.schedule_create_volumes(self.context.elevated(),
- request_spec,
- availability_zone="nova:host_5")
-
- self.assertEqual(len(scheduled_volumes), 3)
- self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_5')
- self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_5')
- self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_5')
-
- def test_vsa_sched_create_volumes_partition(self):
- global scheduled_volumes
- scheduled_volumes = []
- self._set_service_states(host_num=5,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=1,
- exclude_host_list=['host_0', 'host_2'])
- prev = self._generate_default_service_states()
- request_spec = self._get_vol_creation_request(num_vols=3,
- drive_ix=3,
- size=50)
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- self.assertEqual(len(scheduled_volumes), 3)
- self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_1')
- self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_3')
- self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_4')
-
- cur = self._get_service_states()
- for host in ['host_1', 'host_3', 'host_4']:
- cur_dtype = cur[host]['volume']['drive_qos_info']['name_3']
- prev_dtype = prev[host]['volume']['drive_qos_info']['name_3']
-
- self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
- self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
- prev_dtype['FullDrive']['NumFreeDrives'] - 1)
- self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
- prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
-
- self.assertEqual(prev_dtype['PartitionDrive']
- ['NumOccupiedPartitions'], 0)
- self.assertEqual(cur_dtype['PartitionDrive']
- ['NumOccupiedPartitions'], 1)
- self.assertEqual(cur_dtype['PartitionDrive']
- ['NumFreePartitions'], 5)
-
- self.assertEqual(prev_dtype['PartitionDrive']
- ['NumFreePartitions'], 0)
- self.assertEqual(prev_dtype['PartitionDrive']
- ['PartitionSize'], 0)
-
- def test_vsa_sched_create_single_volume_az(self):
- global scheduled_volume
- scheduled_volume = {}
-
- def _fake_volume_get_az(context, volume_id):
- LOG.debug(_("Test: Volume get: id=%(volume_id)s"), locals())
- return {'id': volume_id, 'availability_zone': 'nova:host_3'}
-
- self.stubs.Set(db, 'volume_get', _fake_volume_get_az)
- self.stubs.Set(db, 'service_get_by_args',
- self._fake_service_get_by_args)
- self.stubs.Set(utils,
- 'service_is_up', self._fake_service_is_up_True)
-
- self.driver.schedule_create_volume(self.context.elevated(),
- 123, availability_zone=None)
-
- self.assertEqual(scheduled_volume['id'], 123)
- self.assertEqual(scheduled_volume['host'], 'host_3')
-
- def test_vsa_sched_create_single_non_vsa_volume(self):
- global scheduled_volume
- scheduled_volume = {}
-
- global global_volume
- global_volume = {}
- global_volume['volume_type_id'] = None
-
- self.assertRaises(exception.NoValidHost,
- self.driver.schedule_create_volume,
- self.context,
- 123,
- availability_zone=None)
-
- def test_vsa_sched_create_single_volume(self):
- global scheduled_volume
- scheduled_volume = {}
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=10,
- exclude_host_list=['host_0', 'host_1'])
- prev = self._generate_default_service_states()
-
- global global_volume
- global_volume = {}
-
- drive_ix = 2
- name = 'name_' + str(drive_ix)
- volume_types.create(self.context.elevated(), name,
- extra_specs={'type': 'vsa_drive',
- 'drive_name': name,
- 'drive_type': 'type_' + str(drive_ix),
- 'drive_size': 1 + 100 * (drive_ix)})
- self.created_types_lst.append(name)
- volume_type = volume_types.get_volume_type_by_name(self.context, name)
-
- global_volume['volume_type_id'] = volume_type['id']
- global_volume['size'] = 0
-
- self.driver.schedule_create_volume(self.context,
- 123, availability_zone=None)
-
- self.assertEqual(scheduled_volume['id'], 123)
- self.assertEqual(scheduled_volume['host'], 'host_2')
-
-
-class VsaSchedulerTestCaseMostAvail(VsaSchedulerTestCase):
-
- driver_cls = FakeVsaMostAvailCapacityScheduler
-
- def test_vsa_sched_create_single_volume(self):
- global scheduled_volume
- scheduled_volume = {}
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=10,
- exclude_host_list=['host_0', 'host_1'])
- prev = self._generate_default_service_states()
-
- global global_volume
- global_volume = {}
-
- drive_ix = 2
- name = 'name_' + str(drive_ix)
- volume_types.create(self.context.elevated(), name,
- extra_specs={'type': 'vsa_drive',
- 'drive_name': name,
- 'drive_type': 'type_' + str(drive_ix),
- 'drive_size': 1 + 100 * (drive_ix)})
- self.created_types_lst.append(name)
- volume_type = volume_types.get_volume_type_by_name(self.context, name)
-
- global_volume['volume_type_id'] = volume_type['id']
- global_volume['size'] = 0
-
- self.driver.schedule_create_volume(self.context,
- 123, availability_zone=None)
-
- self.assertEqual(scheduled_volume['id'], 123)
- self.assertEqual(scheduled_volume['host'], 'host_9')
-
- def test_vsa_sched_create_volumes_simple(self):
- global scheduled_volumes
- scheduled_volumes = []
- self._set_service_states(host_num=10,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=10,
- exclude_host_list=['host_1', 'host_3'])
- prev = self._generate_default_service_states()
- request_spec = self._get_vol_creation_request(num_vols=3, drive_ix=2)
-
- self._print_service_states()
-
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- self.assertEqual(len(scheduled_volumes), 3)
- self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_9')
- self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_8')
- self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_7')
-
- cur = self._get_service_states()
- for host in ['host_9', 'host_8', 'host_7']:
- cur_dtype = cur[host]['volume']['drive_qos_info']['name_2']
- prev_dtype = prev[host]['volume']['drive_qos_info']['name_2']
- self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
- self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
- prev_dtype['FullDrive']['NumFreeDrives'] - 1)
- self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
- prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
-
- def test_vsa_sched_create_volumes_partition(self):
- global scheduled_volumes
- scheduled_volumes = []
- self._set_service_states(host_num=5,
- drive_type_start_ix=0,
- drive_type_num=5,
- init_num_drives=1,
- exclude_host_list=['host_0', 'host_2'])
- prev = self._generate_default_service_states()
- request_spec = self._get_vol_creation_request(num_vols=3,
- drive_ix=3,
- size=50)
- self.driver.schedule_create_volumes(self.context,
- request_spec,
- availability_zone=None)
-
- self.assertEqual(len(scheduled_volumes), 3)
- self.assertEqual(scheduled_volumes[0]['vol']['host'], 'host_4')
- self.assertEqual(scheduled_volumes[1]['vol']['host'], 'host_3')
- self.assertEqual(scheduled_volumes[2]['vol']['host'], 'host_1')
-
- cur = self._get_service_states()
- for host in ['host_1', 'host_3', 'host_4']:
- cur_dtype = cur[host]['volume']['drive_qos_info']['name_3']
- prev_dtype = prev[host]['volume']['drive_qos_info']['name_3']
-
- self.assertEqual(cur_dtype['DriveType'], prev_dtype['DriveType'])
- self.assertEqual(cur_dtype['FullDrive']['NumFreeDrives'],
- prev_dtype['FullDrive']['NumFreeDrives'] - 1)
- self.assertEqual(cur_dtype['FullDrive']['NumOccupiedDrives'],
- prev_dtype['FullDrive']['NumOccupiedDrives'] + 1)
-
- self.assertEqual(prev_dtype['PartitionDrive']
- ['NumOccupiedPartitions'], 0)
- self.assertEqual(cur_dtype['PartitionDrive']
- ['NumOccupiedPartitions'], 1)
- self.assertEqual(cur_dtype['PartitionDrive']
- ['NumFreePartitions'], 5)
- self.assertEqual(prev_dtype['PartitionDrive']
- ['NumFreePartitions'], 0)
- self.assertEqual(prev_dtype['PartitionDrive']
- ['PartitionSize'], 0)
diff --git a/nova/tests/test_vsa.py b/nova/tests/test_vsa.py
deleted file mode 100644
index cfc57d11c..000000000
--- a/nova/tests/test_vsa.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# Copyright 2011 OpenStack LLC.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import base64
-
-from xml.etree import ElementTree
-
-from nova import context
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova import vsa
-from nova.volume import volume_types
-from nova.vsa import utils as vsa_utils
-
-import nova.image.fake
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-
-
-class VsaTestCase(test.TestCase):
-
- def setUp(self):
- super(VsaTestCase, self).setUp()
- self.vsa_api = vsa.api.API()
-
- self.flags(quota_volumes=100, quota_gigabytes=10000)
-
- self.context = context.get_admin_context()
-
- volume_types.create(self.context,
- 'SATA_500_7200',
- extra_specs={'type': 'vsa_drive',
- 'drive_name': 'SATA_500_7200',
- 'drive_type': 'SATA',
- 'drive_size': '500',
- 'drive_rpm': '7200'})
-
- def fake_show_by_name(meh, context, name):
- if name == 'wrong_image_name':
- LOG.debug(_("Test: Emulate wrong VSA name. Raise"))
- raise exception.ImageNotFound
- return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
-
- self.stubs.Set(nova.image.fake._FakeImageService,
- 'show_by_name',
- fake_show_by_name)
-
- def test_vsa_create_delete_defaults(self):
- param = {'display_name': 'VSA name test'}
- vsa_ref = self.vsa_api.create(self.context, **param)
- self.assertEqual(vsa_ref['display_name'], param['display_name'])
- self.vsa_api.delete(self.context, vsa_ref['id'])
-
- def test_vsa_create_delete_check_in_db(self):
- vsa_list1 = self.vsa_api.get_all(self.context)
- vsa_ref = self.vsa_api.create(self.context)
- vsa_list2 = self.vsa_api.get_all(self.context)
- self.assertEqual(len(vsa_list2), len(vsa_list1) + 1)
-
- self.vsa_api.delete(self.context, vsa_ref['id'])
- vsa_list3 = self.vsa_api.get_all(self.context)
- self.assertEqual(len(vsa_list3), len(vsa_list2) - 1)
-
- def test_vsa_create_delete_high_vc_count(self):
- param = {'vc_count': FLAGS.max_vcs_in_vsa + 1}
- vsa_ref = self.vsa_api.create(self.context, **param)
- self.assertEqual(vsa_ref['vc_count'], FLAGS.max_vcs_in_vsa)
- self.vsa_api.delete(self.context, vsa_ref['id'])
-
- def test_vsa_create_wrong_image_name(self):
- param = {'image_name': 'wrong_image_name'}
- self.assertRaises(exception.ImageNotFound,
- self.vsa_api.create, self.context, **param)
-
- def test_vsa_create_db_error(self):
-
- def fake_vsa_create(context, options):
- LOG.debug(_("Test: Emulate DB error. Raise"))
- raise exception.Error
-
- self.stubs.Set(nova.db, 'vsa_create', fake_vsa_create)
- self.assertRaises(exception.Error,
- self.vsa_api.create, self.context)
-
- def test_vsa_create_wrong_storage_params(self):
- vsa_list1 = self.vsa_api.get_all(self.context)
- param = {'storage': [{'stub': 1}]}
- self.assertRaises(exception.InvalidVolumeType,
- self.vsa_api.create, self.context, **param)
- vsa_list2 = self.vsa_api.get_all(self.context)
- self.assertEqual(len(vsa_list2), len(vsa_list1))
-
- param = {'storage': [{'drive_name': 'wrong name'}]}
- self.assertRaises(exception.InvalidVolumeType,
- self.vsa_api.create, self.context, **param)
-
- def test_vsa_create_with_storage(self, multi_vol_creation=True):
- """Test creation of VSA with BE storage"""
-
- self.flags(vsa_multi_vol_creation=multi_vol_creation)
-
- param = {'storage': [{'drive_name': 'SATA_500_7200',
- 'num_drives': 3}]}
- vsa_ref = self.vsa_api.create(self.context, **param)
- self.assertEqual(vsa_ref['vol_count'], 3)
- self.vsa_api.delete(self.context, vsa_ref['id'])
-
- param = {'storage': [{'drive_name': 'SATA_500_7200',
- 'num_drives': 3}],
- 'shared': True}
- vsa_ref = self.vsa_api.create(self.context, **param)
- self.assertEqual(vsa_ref['vol_count'], 15)
- self.vsa_api.delete(self.context, vsa_ref['id'])
-
- def test_vsa_create_with_storage_single_volumes(self):
- self.test_vsa_create_with_storage(multi_vol_creation=False)
-
- def test_vsa_update(self):
- vsa_ref = self.vsa_api.create(self.context)
-
- param = {'vc_count': FLAGS.max_vcs_in_vsa + 1}
- vsa_ref = self.vsa_api.update(self.context, vsa_ref['id'], **param)
- self.assertEqual(vsa_ref['vc_count'], FLAGS.max_vcs_in_vsa)
-
- param = {'vc_count': 2}
- vsa_ref = self.vsa_api.update(self.context, vsa_ref['id'], **param)
- self.assertEqual(vsa_ref['vc_count'], 2)
-
- self.vsa_api.delete(self.context, vsa_ref['id'])
-
- def test_vsa_generate_user_data(self):
-
- self.flags(vsa_multi_vol_creation=False)
- param = {'display_name': 'VSA name test',
- 'display_description': 'VSA desc test',
- 'vc_count': 2,
- 'storage': [{'drive_name': 'SATA_500_7200',
- 'num_drives': 3}]}
- vsa_ref = self.vsa_api.create(self.context, **param)
- volumes = self.vsa_api.get_all_vsa_drives(self.context,
- vsa_ref['id'])
-
- user_data = vsa_utils.generate_user_data(vsa_ref, volumes)
- user_data = base64.b64decode(user_data)
-
- LOG.debug(_("Test: user_data = %s"), user_data)
-
- elem = ElementTree.fromstring(user_data)
- self.assertEqual(elem.findtext('name'),
- param['display_name'])
- self.assertEqual(elem.findtext('description'),
- param['display_description'])
- self.assertEqual(elem.findtext('vc_count'),
- str(param['vc_count']))
-
- self.vsa_api.delete(self.context, vsa_ref['id'])
diff --git a/nova/tests/test_vsa_volumes.py b/nova/tests/test_vsa_volumes.py
deleted file mode 100644
index adf22077c..000000000
--- a/nova/tests/test_vsa_volumes.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# Copyright 2011 OpenStack LLC.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from nova import exception
-from nova import flags
-from nova.vsa import api as vsa_api
-from nova import volume
-from nova import context
-from nova import test
-from nova import log as logging
-import nova.image.fake
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-
-
-class VsaVolumesTestCase(test.TestCase):
-
- def setUp(self):
- super(VsaVolumesTestCase, self).setUp()
- self.vsa_api = vsa_api.API()
- self.volume_api = volume.API()
- self.context = context.get_admin_context()
-
- self.default_vol_type = self.vsa_api.get_vsa_volume_type(self.context)
-
- def fake_show_by_name(meh, context, name):
- return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
-
- self.stubs.Set(nova.image.fake._FakeImageService,
- 'show_by_name',
- fake_show_by_name)
-
- param = {'display_name': 'VSA name test'}
- vsa_ref = self.vsa_api.create(self.context, **param)
- self.vsa_id = vsa_ref['id']
-
- def tearDown(self):
- if self.vsa_id:
- self.vsa_api.delete(self.context, self.vsa_id)
- super(VsaVolumesTestCase, self).tearDown()
-
- def _default_volume_param(self):
- return {
- 'size': 1,
- 'snapshot': None,
- 'name': 'Test volume name',
- 'description': 'Test volume desc name',
- 'volume_type': self.default_vol_type,
- 'metadata': {'from_vsa_id': self.vsa_id}
- }
-
- def _get_all_volumes_by_vsa(self):
- return self.volume_api.get_all(self.context,
- search_opts={'metadata': {"from_vsa_id": str(self.vsa_id)}})
-
- def test_vsa_volume_create_delete(self):
- """ Check if volume properly created and deleted. """
- volume_param = self._default_volume_param()
- volume_ref = self.volume_api.create(self.context, **volume_param)
-
- self.assertEqual(volume_ref['display_name'],
- volume_param['name'])
- self.assertEqual(volume_ref['display_description'],
- volume_param['description'])
- self.assertEqual(volume_ref['size'],
- volume_param['size'])
- self.assertEqual(volume_ref['status'],
- 'creating')
-
- vols2 = self._get_all_volumes_by_vsa()
- self.assertEqual(1, len(vols2))
- volume_ref = vols2[0]
-
- self.assertEqual(volume_ref['display_name'],
- volume_param['name'])
- self.assertEqual(volume_ref['display_description'],
- volume_param['description'])
- self.assertEqual(volume_ref['size'],
- volume_param['size'])
- self.assertEqual(volume_ref['status'],
- 'creating')
-
- self.volume_api.update(self.context, volume_ref,
- {'status': 'available', 'host': 'fake'})
- volume_ref = self.volume_api.get(self.context, volume_ref['id'])
- self.volume_api.delete(self.context, volume_ref)
-
- vols3 = self._get_all_volumes_by_vsa()
- self.assertEqual(1, len(vols2))
- volume_ref = vols3[0]
- self.assertEqual(volume_ref['status'],
- 'deleting')
-
- def test_vsa_volume_delete_nonavail_volume(self):
- """ Check volume deletion in different states. """
- volume_param = self._default_volume_param()
- volume_ref = self.volume_api.create(self.context, **volume_param)
-
- self.volume_api.update(self.context, volume_ref,
- {'status': 'in-use', 'host': 'fake'})
- volume_ref = self.volume_api.get(self.context, volume_ref['id'])
- self.assertRaises(exception.InvalidVolume,
- self.volume_api.delete,
- self.context, volume_ref)
-
- def test_vsa_volume_delete_vsa_with_volumes(self):
- """ Check volume deleton in different states. """
-
- vols1 = self._get_all_volumes_by_vsa()
- for i in range(3):
- volume_param = self._default_volume_param()
- volume_ref = self.volume_api.create(self.context, **volume_param)
-
- vols2 = self._get_all_volumes_by_vsa()
- self.assertEqual(len(vols1) + 3, len(vols2))
-
- self.vsa_api.delete(self.context, self.vsa_id)
-
- vols3 = self._get_all_volumes_by_vsa()
- self.assertEqual(len(vols1), len(vols3))
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index a8ed76a97..ffdd1f520 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -20,9 +20,7 @@ Drivers for volumes.
"""
-import os
import time
-from xml.etree import ElementTree
from nova import exception
from nova import flags
@@ -30,7 +28,6 @@ from nova import log as logging
from nova.openstack.common import cfg
from nova import utils
from nova.volume import iscsi
-from nova.volume import volume_types
LOG = logging.getLogger(__name__)
@@ -697,324 +694,5 @@ class LoggingVolumeDriver(VolumeDriver):
return matches
-class ZadaraBEDriver(ISCSIDriver):
- """Performs actions to configure Zadara BE module."""
-
- def _is_vsa_volume(self, volume):
- return volume_types.is_vsa_volume(volume['volume_type_id'])
-
- def _is_vsa_drive(self, volume):
- return volume_types.is_vsa_drive(volume['volume_type_id'])
-
- def _not_vsa_volume_or_drive(self, volume):
- """Returns True if volume is not VSA BE volume."""
- if not volume_types.is_vsa_object(volume['volume_type_id']):
- LOG.debug(_("\tVolume %s is NOT VSA volume"), volume['name'])
- return True
- else:
- return False
-
- def check_for_setup_error(self):
- """No setup necessary for Zadara BE."""
- pass
-
- """ Volume Driver methods """
- def create_volume(self, volume):
- """Creates BE volume."""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).create_volume(volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s creation - do nothing"),
- volume['name'])
- return
-
- if int(volume['size']) == 0:
- sizestr = '0' # indicates full-partition
- else:
- sizestr = '%s' % (int(volume['size']) << 30) # size in bytes
-
- # Set the qos-str to default type sas
- qosstr = 'SAS_1000'
- volume_type = volume_types.get_volume_type(None,
- volume['volume_type_id'])
- if volume_type is not None:
- qosstr = '_'.join([volume_type['extra_specs']['drive_type'],
- volume_type['extra_specs']['drive_size']])
-
- vsa_id = None
- for i in volume.get('volume_metadata'):
- if i['key'] == 'to_vsa_id':
- vsa_id = i['value']
- break
-
- try:
- self._execute('/var/lib/zadara/bin/zadara_sncfg',
- 'create_qospart',
- '--qos', qosstr,
- '--pname', volume['name'],
- '--psize', sizestr,
- '--vsaid', vsa_id,
- run_as_root=True,
- check_exit_code=0)
- except exception.ProcessExecutionError:
- LOG.debug(_("VSA BE create_volume for %s failed"), volume['name'])
- raise
-
- LOG.debug(_("VSA BE create_volume for %s succeeded"), volume['name'])
-
- def delete_volume(self, volume):
- """Deletes BE volume."""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).delete_volume(volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s deletion - do nothing"),
- volume['name'])
- return
-
- try:
- self._execute('/var/lib/zadara/bin/zadara_sncfg',
- 'delete_partition',
- '--pname', volume['name'],
- run_as_root=True,
- check_exit_code=0)
- except exception.ProcessExecutionError:
- LOG.debug(_("VSA BE delete_volume for %s failed"), volume['name'])
- return
-
- LOG.debug(_("VSA BE delete_volume for %s suceeded"), volume['name'])
-
- def _discover_volume(self, context, volume):
- """Discover volume on a remote host."""
- iscsi_properties = self._get_iscsi_properties(volume)
-
- if not iscsi_properties['target_discovered']:
- self._run_iscsiadm(iscsi_properties, ('--op', 'new'))
-
- if iscsi_properties.get('auth_method'):
- self._iscsiadm_update(iscsi_properties,
- "node.session.auth.authmethod",
- iscsi_properties['auth_method'])
- self._iscsiadm_update(iscsi_properties,
- "node.session.auth.username",
- iscsi_properties['auth_username'])
- self._iscsiadm_update(iscsi_properties,
- "node.session.auth.password",
- iscsi_properties['auth_password'])
-
- self._run_iscsiadm(iscsi_properties, ("--login", ))
-
- self._iscsiadm_update(iscsi_properties, "node.startup", "automatic")
-
- mount_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
- (iscsi_properties['target_portal'],
- iscsi_properties['target_iqn'],
- iscsi_properties['target_lun']))
-
- # The /dev/disk/by-path/... node is not always present immediately
- # TODO(justinsb): This retry-with-delay is a pattern, move to utils?
- tries = 0
- while not os.path.exists(mount_device):
- if tries >= FLAGS.num_iscsi_scan_tries:
- raise exception.Error(_("iSCSI device not found at %s") %
- (mount_device))
-
- LOG.warn(_("ISCSI volume not yet found at: %(mount_device)s. "
- "Will rescan & retry. Try number: %(tries)s") %
- locals())
-
- # The rescan isn't documented as being necessary(?), but it helps
- self._run_iscsiadm(iscsi_properties, ("--rescan", ))
-
- tries = tries + 1
- if not os.path.exists(mount_device):
- time.sleep(tries ** 2)
-
- if tries != 0:
- LOG.debug(_("Found iSCSI node %(mount_device)s "
- "(after %(tries)s rescans)") %
- locals())
-
- return mount_device
-
- def local_path(self, volume):
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).local_path(volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s local path call - call discover"),
- volume['name'])
- # NOTE(vish): Copied discover from iscsi_driver since it is used
- # but this should probably be refactored into a common
- # area because it is used in libvirt driver.
- return self._discover_volume(None, volume)
-
- raise exception.Error(_("local_path not supported"))
-
- def ensure_export(self, context, volume):
- """ensure BE export for a volume"""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).ensure_export(context, volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s ensure export - do nothing"),
- volume['name'])
- return
-
- try:
- iscsi_target = self.db.volume_get_iscsi_target_num(context,
- volume['id'])
- except exception.NotFound:
- LOG.info(_("Skipping ensure_export. No iscsi_target " +
- "provisioned for volume: %d"), volume['id'])
- return
-
- try:
- ret = self._common_be_export(context, volume, iscsi_target)
- except exception.ProcessExecutionError:
- return
- return ret
-
- def create_export(self, context, volume):
- """create BE export for a volume"""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).create_export(context, volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s create export - do nothing"),
- volume['name'])
- return
-
- self._ensure_iscsi_targets(context, volume['host'])
- iscsi_target = self.db.volume_allocate_iscsi_target(context,
- volume['id'],
- volume['host'])
- try:
- ret = self._common_be_export(context, volume, iscsi_target)
- except Exception:
- raise exception.ProcessExecutionError
- return ret
-
- def remove_export(self, context, volume):
- """Removes BE export for a volume."""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).remove_export(context, volume)
-
- if self._is_vsa_volume(volume):
- LOG.debug(_("\tFE VSA Volume %s remove export - do nothing"),
- volume['name'])
- return
-
- try:
- iscsi_target = self.db.volume_get_iscsi_target_num(context,
- volume['id'])
- except exception.NotFound:
- LOG.info(_("Skipping remove_export. No iscsi_target " +
- "provisioned for volume: %d"), volume['id'])
- return
-
- try:
- self._execute('/var/lib/zadara/bin/zadara_sncfg',
- 'remove_export',
- '--pname', volume['name'],
- '--tid', iscsi_target,
- run_as_root=True,
- check_exit_code=0)
- except exception.ProcessExecutionError:
- LOG.debug(_("VSA BE remove_export for %s failed"), volume['name'])
- return
-
- def create_snapshot(self, snapshot):
- """Nothing required for snapshot"""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).create_snapshot(volume)
-
- pass
-
- def delete_snapshot(self, snapshot):
- """Nothing required to delete a snapshot"""
- if self._not_vsa_volume_or_drive(volume):
- return super(ZadaraBEDriver, self).delete_snapshot(volume)
-
- pass
-
- """ Internal BE Volume methods """
- def _common_be_export(self, context, volume, iscsi_target):
- """
- Common logic that asks zadara_sncfg to setup iSCSI target/lun for
- this volume
- """
- (out, err) = self._execute('/var/lib/zadara/bin/zadara_sncfg',
- 'create_export',
- '--pname', volume['name'],
- '--tid', iscsi_target,
- run_as_root=True,
- check_exit_code=0)
-
- result_xml = ElementTree.fromstring(out)
- response_node = result_xml.find("Sn")
- if response_node is None:
- msg = "Malformed response from zadara_sncfg"
- raise exception.Error(msg)
-
- sn_ip = response_node.findtext("SnIp")
- sn_iqn = response_node.findtext("IqnName")
-
- model_update = {}
- model_update['provider_location'] = _iscsi_location(
- sn_ip, iscsi_target, sn_iqn)
- return model_update
-
- def _get_qosgroup_summary(self):
- """gets the list of qosgroups from Zadara BE"""
- try:
- (out, err) = self._execute('/var/lib/zadara/bin/zadara_sncfg',
- 'get_qosgroups_xml',
- run_as_root=True,
- check_exit_code=0)
- except exception.ProcessExecutionError:
- LOG.debug(_("Failed to retrieve QoS info"))
- return {}
-
- qos_groups = {}
- result_xml = ElementTree.fromstring(out)
- for element in result_xml.findall('QosGroup'):
- qos_group = {}
- # get the name of the group.
- # If we cannot find it, forget this element
- group_name = element.findtext("Name")
- if not group_name:
- continue
-
- # loop through all child nodes & fill up attributes of this group
- for child in element.getchildren():
- # two types of elements - property of qos-group & sub property
- # classify them accordingly
- if child.text:
- qos_group[child.tag] = (int(child.text)
- if child.text.isdigit()
- else child.text)
- else:
- subelement = {}
- for subchild in child.getchildren():
- subelement[subchild.tag] = (int(subchild.text)
- if subchild.text.isdigit()
- else subchild.text)
- qos_group[child.tag] = subelement
-
- # Now add this group to the master qos_groups
- qos_groups[group_name] = qos_group
-
- return qos_groups
-
- def get_volume_stats(self, refresh=False):
- """Return the current state of the volume service. If 'refresh' is
- True, run the update first."""
-
- drive_info = self._get_qosgroup_summary()
- return {'drive_qos_info': drive_info}
-
-
def _iscsi_location(ip, target, iqn, lun=None):
return "%s:%s,%s %s %s" % (ip, FLAGS.iscsi_port, target, iqn, lun)
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 9da7c8be8..ada38871f 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -136,36 +136,15 @@ class VolumeManager(manager.SchedulerDependentManager):
with utils.save_and_reraise_exception():
self.db.volume_update(context,
volume_ref['id'], {'status': 'error'})
- self._notify_vsa(context, volume_ref, 'error')
now = utils.utcnow()
self.db.volume_update(context,
volume_ref['id'], {'status': 'available',
'launched_at': now})
LOG.debug(_("volume %s: created successfully"), volume_ref['name'])
- self._notify_vsa(context, volume_ref, 'available')
self._reset_stats()
return volume_id
- def _notify_vsa(self, context, volume_ref, status):
- if volume_ref['volume_type_id'] is None:
- return
-
- if volume_types.is_vsa_drive(volume_ref['volume_type_id']):
- vsa_id = None
- for i in volume_ref.get('volume_metadata'):
- if i['key'] == 'to_vsa_id':
- vsa_id = int(i['value'])
- break
-
- if vsa_id:
- rpc.cast(context,
- FLAGS.vsa_topic,
- {"method": "vsa_volume_created",
- "args": {"vol_id": volume_ref['id'],
- "vsa_id": vsa_id,
- "status": status}})
-
def delete_volume(self, context, volume_id):
"""Deletes and unexports volume."""
context = context.elevated()
diff --git a/nova/volume/volume_types.py b/nova/volume/volume_types.py
index 1e1ce2191..0b8208663 100644
--- a/nova/volume/volume_types.py
+++ b/nova/volume/volume_types.py
@@ -123,24 +123,3 @@ def is_key_value_present(volume_type_id, key, value, volume_type=None):
return False
else:
return True
-
-
-def is_vsa_drive(volume_type_id, volume_type=None):
- return is_key_value_present(volume_type_id,
- 'type', 'vsa_drive', volume_type)
-
-
-def is_vsa_volume(volume_type_id, volume_type=None):
- return is_key_value_present(volume_type_id,
- 'type', 'vsa_volume', volume_type)
-
-
-def is_vsa_object(volume_type_id):
- if volume_type_id is None:
- return False
-
- volume_type = get_volume_type(context.get_admin_context(),
- volume_type_id)
-
- return (is_vsa_drive(volume_type_id, volume_type) or
- is_vsa_volume(volume_type_id, volume_type))
diff --git a/nova/vsa/__init__.py b/nova/vsa/__init__.py
deleted file mode 100644
index 8b6ff0457..000000000
--- a/nova/vsa/__init__.py
+++ /dev/null
@@ -1,16 +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.
diff --git a/nova/vsa/api.py b/nova/vsa/api.py
deleted file mode 100644
index 01da599b0..000000000
--- a/nova/vsa/api.py
+++ /dev/null
@@ -1,412 +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.
-
-"""
-Handles all requests relating to Virtual Storage Arrays (VSAs).
-
-Experimental code. Requires special VSA image.
-
-For assistance and guidelines pls contact
-Zadara Storage Inc & OpenStack community
-"""
-
-from nova import compute
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova.openstack.common import cfg
-from nova import rpc
-from nova import volume
-from nova.compute import instance_types
-from nova.db import base
-from nova.volume import volume_types
-
-
-class VsaState:
- CREATING = 'creating' # VSA creating (not ready yet)
- LAUNCHING = 'launching' # Launching VCs (all BE volumes were created)
- CREATED = 'created' # VSA fully created and ready for use
- PARTIAL = 'partial' # Some BE drives were allocated
- FAILED = 'failed' # Some BE storage allocations failed
- DELETING = 'deleting' # VSA started the deletion procedure
-
-
-vsa_opts = [
- cfg.StrOpt('vsa_ec2_access_key',
- default=None,
- help='EC2 access key used by VSA for accessing nova'),
- cfg.StrOpt('vsa_ec2_user_id',
- default=None,
- help='User ID used by VSA for accessing nova'),
- cfg.BoolOpt('vsa_multi_vol_creation',
- default=True,
- help='Ask scheduler to create multiple volumes in one call'),
- cfg.StrOpt('vsa_volume_type_name',
- default='VSA volume type',
- help='Name of volume type associated with FE VSA volumes'),
- ]
-
-FLAGS = flags.FLAGS
-FLAGS.register_opts(vsa_opts)
-
-LOG = logging.getLogger(__name__)
-
-
-class API(base.Base):
- """API for interacting with the VSA manager."""
-
- def __init__(self, compute_api=None, volume_api=None, **kwargs):
- self.compute_api = compute_api or compute.API()
- self.volume_api = volume_api or volume.API()
- super(API, self).__init__(**kwargs)
-
- def _check_volume_type_correctness(self, vol_type):
- if (vol_type.get('extra_specs') is None or
- vol_type['extra_specs'].get('type') != 'vsa_drive' or
- vol_type['extra_specs'].get('drive_type') is None or
- vol_type['extra_specs'].get('drive_size') is None):
-
- msg = _("invalid drive data")
- raise exception.InvalidVolumeType(reason=msg)
-
- def _get_default_vsa_instance_type(self):
- return instance_types.get_instance_type_by_name(
- FLAGS.default_vsa_instance_type)
-
- def _check_storage_parameters(self, context, vsa_name, storage,
- shared, first_index=0):
- """
- Translates storage array of disks to the list of volumes
- :param storage: List of dictionaries with following keys:
- disk_name, num_disks, size
- :param shared: Specifies if storage is dedicated or shared.
- For shared storage disks split into partitions
- """
- volume_params = []
- for node in storage:
-
- name = node.get('drive_name', None)
- num_disks = node.get('num_drives', 1)
-
- if name is None:
- msg = _("drive_name not defined")
- raise exception.InvalidVolumeType(reason=msg)
-
- try:
- vol_type = volume_types.get_volume_type_by_name(context, name)
- except exception.NotFound:
- msg = _("invalid drive type name %s")
- raise exception.InvalidVolumeType(reason=msg % name)
-
- self._check_volume_type_correctness(vol_type)
-
- # if size field present - override disk size specified in DB
- size = int(node.get('size',
- vol_type['extra_specs'].get('drive_size')))
-
- if shared:
- part_size = FLAGS.vsa_part_size_gb
- total_capacity = num_disks * size
- num_volumes = total_capacity / part_size
- size = part_size
- else:
- num_volumes = num_disks
- size = 0 # special handling for full drives
-
- for i in range(num_volumes):
- volume_name = "drive-%03d" % first_index
- first_index += 1
- volume_desc = 'BE volume for VSA %s type %s' % (vsa_name, name)
- volume = {
- 'size': size,
- 'name': volume_name,
- 'description': volume_desc,
- 'volume_type_id': vol_type['id'],
- }
- volume_params.append(volume)
-
- return volume_params
-
- def create(self, context, display_name='', display_description='',
- vc_count=1, instance_type=None, image_name=None,
- availability_zone=None, storage=[], shared=None):
- """Provision VSA instance with compute instances and volumes
-
- :param storage: List of dictionaries with following keys:
- disk_name, num_disks, size
- :param shared: Specifies if storage is dedicated or shared.
- For shared storage disks split into partitions
- """
-
- LOG.info(_("*** Experimental VSA code ***"))
-
- if vc_count > FLAGS.max_vcs_in_vsa:
- LOG.warning(_("Requested number of VCs (%d) is too high."
- " Setting to default"), vc_count)
- vc_count = FLAGS.max_vcs_in_vsa
-
- if instance_type is None:
- instance_type = self._get_default_vsa_instance_type()
-
- if availability_zone is None:
- availability_zone = FLAGS.storage_availability_zone
-
- if storage is None:
- storage = []
-
- if not shared or shared == 'False':
- shared = False
- else:
- shared = True
-
- # check if image is ready before starting any work
- if image_name is None:
- image_name = FLAGS.vc_image_name
-
- image_service = self.compute_api.image_service
- vc_image = image_service.show_by_name(context, image_name)
- vc_image_href = vc_image['id']
-
- options = {
- 'display_name': display_name,
- 'display_description': display_description,
- 'project_id': context.project_id,
- 'availability_zone': availability_zone,
- 'instance_type_id': instance_type['id'],
- 'image_ref': vc_image_href,
- 'vc_count': vc_count,
- 'status': VsaState.CREATING,
- }
- LOG.info(_("Creating VSA: %s") % options)
-
- # create DB entry for VSA instance
- vsa_ref = self.db.vsa_create(context, options)
-
- vsa_id = vsa_ref['id']
- vsa_name = vsa_ref['name']
-
- # check storage parameters
- try:
- volume_params = self._check_storage_parameters(context, vsa_name,
- storage, shared)
- except exception.InvalidVolumeType:
- self.db.vsa_destroy(context, vsa_id)
- raise
-
- # after creating DB entry, re-check and set some defaults
- updates = {}
- if (not hasattr(vsa_ref, 'display_name') or
- vsa_ref.display_name is None or
- vsa_ref.display_name == ''):
- updates['display_name'] = display_name = vsa_name
- updates['vol_count'] = len(volume_params)
- vsa_ref = self.update(context, vsa_id, **updates)
-
- # create volumes
- if FLAGS.vsa_multi_vol_creation:
- if len(volume_params) > 0:
- request_spec = {
- 'num_volumes': len(volume_params),
- 'vsa_id': str(vsa_id),
- 'volumes': volume_params,
- }
-
- rpc.cast(context,
- FLAGS.scheduler_topic,
- {"method": "create_volumes",
- "args": {"topic": FLAGS.volume_topic,
- "request_spec": request_spec,
- "availability_zone": availability_zone}})
- else:
- # create BE volumes one-by-one
- for vol in volume_params:
- try:
- vol_name = vol['name']
- vol_size = vol['size']
- vol_type_id = vol['volume_type_id']
- LOG.debug(_("VSA ID %(vsa_id)d %(vsa_name)s: Create "
- "volume %(vol_name)s, %(vol_size)d GB, "
- "type %(vol_type_id)s"), locals())
-
- vol_type = volume_types.get_volume_type(context,
- vol['volume_type_id'])
-
- vol_ref = self.volume_api.create(context,
- vol_size,
- vol_name,
- vol['description'],
- None,
- volume_type=vol_type,
- metadata=dict(to_vsa_id=str(vsa_id)),
- availability_zone=availability_zone)
- except Exception:
- self.update_vsa_status(context, vsa_id,
- status=VsaState.PARTIAL)
- raise
-
- if len(volume_params) == 0:
- # No BE volumes - ask VSA manager to start VCs
- rpc.cast(context,
- FLAGS.vsa_topic,
- {"method": "create_vsa",
- "args": {"vsa_id": str(vsa_id)}})
-
- return vsa_ref
-
- def update_vsa_status(self, context, vsa_id, status):
- updates = dict(status=status)
- LOG.info(_("VSA ID %(vsa_id)d: Update VSA status to %(status)s"),
- locals())
- return self.update(context, vsa_id, **updates)
-
- def update(self, context, vsa_id, **kwargs):
- """Updates the VSA instance in the datastore.
-
- :param context: The security context
- :param vsa_id: ID of the VSA instance to update
- :param kwargs: All additional keyword args are treated
- as data fields of the instance to be
- updated
-
- :returns: None
- """
- LOG.info(_("VSA ID %(vsa_id)d: Update VSA call"), locals())
-
- updatable_fields = ['status', 'vc_count', 'vol_count',
- 'display_name', 'display_description']
- changes = {}
- for field in updatable_fields:
- if field in kwargs:
- changes[field] = kwargs[field]
-
- vc_count = kwargs.get('vc_count', None)
- if vc_count is not None:
- # VP-TODO(vladimir.p):
- # This request may want to update number of VCs
- # Get number of current VCs and add/delete VCs appropriately
- vsa = self.get(context, vsa_id)
- vc_count = int(vc_count)
- if vc_count > FLAGS.max_vcs_in_vsa:
- LOG.warning(_("Requested number of VCs (%d) is too high."
- " Setting to default"), vc_count)
- vc_count = FLAGS.max_vcs_in_vsa
-
- if vsa['vc_count'] != vc_count:
- self.update_num_vcs(context, vsa, vc_count)
- changes['vc_count'] = vc_count
-
- return self.db.vsa_update(context, vsa_id, changes)
-
- def update_num_vcs(self, context, vsa, vc_count):
- vsa_name = vsa['name']
- old_vc_count = int(vsa['vc_count'])
- if vc_count > old_vc_count:
- add_cnt = vc_count - old_vc_count
- LOG.debug(_("Adding %(add_cnt)s VCs to VSA %(vsa_name)s."),
- locals())
- # VP-TODO(vladimir.p): actual code for adding new VCs
-
- elif vc_count < old_vc_count:
- del_cnt = old_vc_count - vc_count
- LOG.debug(_("Deleting %(del_cnt)s VCs from VSA %(vsa_name)s."),
- locals())
- # VP-TODO(vladimir.p): actual code for deleting extra VCs
-
- def _force_volume_delete(self, ctxt, volume):
- """Delete a volume, bypassing the check that it must be available."""
- host = volume['host']
- if not host:
- # Deleting volume from database and skipping rpc.
- self.db.volume_destroy(ctxt, volume['id'])
- return
-
- rpc.cast(ctxt,
- self.db.queue_get_for(ctxt, FLAGS.volume_topic, host),
- {"method": "delete_volume",
- "args": {"volume_id": volume['id']}})
-
- def delete_vsa_volumes(self, context, vsa_id, direction,
- force_delete=True):
- if direction == "FE":
- volumes = self.get_all_vsa_volumes(context, vsa_id)
- else:
- volumes = self.get_all_vsa_drives(context, vsa_id)
-
- for volume in volumes:
- try:
- vol_name = volume['name']
- LOG.info(_("VSA ID %(vsa_id)s: Deleting %(direction)s "
- "volume %(vol_name)s"), locals())
- self.volume_api.delete(context, volume)
- except exception.InvalidVolume:
- LOG.info(_("Unable to delete volume %s"), volume['name'])
- if force_delete:
- LOG.info(_("VSA ID %(vsa_id)s: Forced delete. "
- "%(direction)s volume %(vol_name)s"), locals())
- self._force_volume_delete(context, volume)
-
- def delete(self, context, vsa_id):
- """Terminate a VSA instance."""
- LOG.info(_("Going to try to terminate VSA ID %s"), vsa_id)
-
- # Delete all FrontEnd and BackEnd volumes
- self.delete_vsa_volumes(context, vsa_id, "FE", force_delete=True)
- self.delete_vsa_volumes(context, vsa_id, "BE", force_delete=True)
-
- # Delete all VC instances
- instances = self.compute_api.get_all(context,
- search_opts={'metadata': dict(vsa_id=str(vsa_id))})
- for instance in instances:
- name = instance['name']
- LOG.debug(_("VSA ID %(vsa_id)s: Delete instance %(name)s"),
- locals())
- self.compute_api.delete(context, instance['id'])
-
- # Delete VSA instance
- self.db.vsa_destroy(context, vsa_id)
-
- def get(self, context, vsa_id):
- rv = self.db.vsa_get(context, vsa_id)
- return rv
-
- def get_all(self, context):
- if context.is_admin:
- return self.db.vsa_get_all(context)
- return self.db.vsa_get_all_by_project(context, context.project_id)
-
- def get_vsa_volume_type(self, context):
- name = FLAGS.vsa_volume_type_name
- try:
- vol_type = volume_types.get_volume_type_by_name(context, name)
- except exception.NotFound:
- volume_types.create(context, name,
- extra_specs=dict(type='vsa_volume'))
- vol_type = volume_types.get_volume_type_by_name(context, name)
-
- return vol_type
-
- def get_all_vsa_instances(self, context, vsa_id):
- return self.compute_api.get_all(context,
- search_opts={'metadata': dict(vsa_id=str(vsa_id))})
-
- def get_all_vsa_volumes(self, context, vsa_id):
- return self.volume_api.get_all(context,
- search_opts={'metadata': dict(from_vsa_id=str(vsa_id))})
-
- def get_all_vsa_drives(self, context, vsa_id):
- return self.volume_api.get_all(context,
- search_opts={'metadata': dict(to_vsa_id=str(vsa_id))})
diff --git a/nova/vsa/connection.py b/nova/vsa/connection.py
deleted file mode 100644
index 8ac8a1dd5..000000000
--- a/nova/vsa/connection.py
+++ /dev/null
@@ -1,25 +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.
-
-"""Abstraction of the underlying connection to VC."""
-
-from nova.vsa import fake
-
-
-def get_connection():
- # Return an object that is able to talk to VCs
- return fake.FakeVcConnection()
diff --git a/nova/vsa/fake.py b/nova/vsa/fake.py
deleted file mode 100644
index d4248ca01..000000000
--- a/nova/vsa/fake.py
+++ /dev/null
@@ -1,22 +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.
-
-
-class FakeVcConnection(object):
-
- def init_host(self, host):
- pass
diff --git a/nova/vsa/manager.py b/nova/vsa/manager.py
deleted file mode 100644
index 1868030f1..000000000
--- a/nova/vsa/manager.py
+++ /dev/null
@@ -1,181 +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.
-
-"""
-Handles all processes relating to Virtual Storage Arrays (VSA).
-
-**Related Flags**
-
-"""
-
-from nova import compute
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import manager
-from nova.openstack.common import cfg
-from nova import volume
-from nova import utils
-from nova.compute import instance_types
-from nova.vsa import utils as vsa_utils
-from nova.vsa import api as vsa_api
-
-vsa_driver_opt = cfg.StrOpt('vsa_driver',
- default='nova.vsa.connection.get_connection',
- help='Driver to use for controlling VSAs')
-
-FLAGS = flags.FLAGS
-FLAGS.register_opt(vsa_driver_opt)
-
-LOG = logging.getLogger(__name__)
-
-
-class VsaManager(manager.SchedulerDependentManager):
- """Manages Virtual Storage Arrays (VSAs)."""
-
- def __init__(self, vsa_driver=None, *args, **kwargs):
- if not vsa_driver:
- vsa_driver = FLAGS.vsa_driver
- self.driver = utils.import_object(vsa_driver)
- self.compute_manager = utils.import_object(FLAGS.compute_manager)
-
- self.compute_api = compute.API()
- self.volume_api = volume.API()
- self.vsa_api = vsa_api.API()
-
- if FLAGS.vsa_ec2_user_id is None or FLAGS.vsa_ec2_access_key is None:
- raise exception.VSANovaAccessParamNotFound()
-
- super(VsaManager, self).__init__(*args, **kwargs)
-
- def init_host(self):
- self.driver.init_host(host=self.host)
- super(VsaManager, self).init_host()
-
- @exception.wrap_exception()
- def create_vsa(self, context, vsa_id):
- """Called by API if there were no BE volumes assigned"""
- LOG.debug(_("Create call received for VSA %s"), vsa_id)
-
- vsa_id = int(vsa_id) # just in case
-
- try:
- vsa = self.vsa_api.get(context, vsa_id)
- except Exception as ex:
- msg = _("Failed to find VSA %(vsa_id)d") % locals()
- LOG.exception(msg)
- return
-
- return self._start_vcs(context, vsa)
-
- @exception.wrap_exception()
- def vsa_volume_created(self, context, vol_id, vsa_id, status):
- """Callback for volume creations"""
- LOG.debug(_("VSA ID %(vsa_id)s: Drive %(vol_id)s created. "
- "Status %(status)s"), locals())
- vsa_id = int(vsa_id) # just in case
-
- # Get all volumes for this VSA
- # check if any of them still in creating phase
- drives = self.vsa_api.get_all_vsa_drives(context, vsa_id)
- for drive in drives:
- if drive['status'] == 'creating':
- vol_name = drive['name']
- vol_disp_name = drive['display_name']
- LOG.debug(_("Drive %(vol_name)s (%(vol_disp_name)s) still "
- "in creating phase - wait"), locals())
- return
-
- try:
- vsa = self.vsa_api.get(context, vsa_id)
- except Exception as ex:
- msg = _("Failed to find VSA %(vsa_id)d") % locals()
- LOG.exception(msg)
- return
-
- if len(drives) != vsa['vol_count']:
- cvol_real = len(drives)
- cvol_exp = vsa['vol_count']
- LOG.debug(_("VSA ID %(vsa_id)d: Not all volumes are created "
- "(%(cvol_real)d of %(cvol_exp)d)"), locals())
- return
-
- # all volumes created (successfully or not)
- return self._start_vcs(context, vsa, drives)
-
- def _start_vcs(self, context, vsa, drives=[]):
- """Start VCs for VSA """
-
- vsa_id = vsa['id']
- if vsa['status'] == vsa_api.VsaState.CREATING:
- self.vsa_api.update_vsa_status(context, vsa_id,
- vsa_api.VsaState.LAUNCHING)
- else:
- return
-
- # in _separate_ loop go over all volumes and mark as "attached"
- has_failed_volumes = False
- for drive in drives:
- vol_name = drive['name']
- vol_disp_name = drive['display_name']
- status = drive['status']
- LOG.info(_("VSA ID %(vsa_id)d: Drive %(vol_name)s "
- "(%(vol_disp_name)s) is in %(status)s state"),
- locals())
- if status == 'available':
- try:
- # self.volume_api.update(context, volume,
- # dict(attach_status="attached"))
- pass
- except Exception as ex:
- msg = _("Failed to update attach status for volume "
- "%(vol_name)s. %(ex)s") % locals()
- LOG.exception(msg)
- else:
- has_failed_volumes = True
-
- if has_failed_volumes:
- LOG.info(_("VSA ID %(vsa_id)d: Delete all BE volumes"), locals())
- self.vsa_api.delete_vsa_volumes(context, vsa_id, "BE", True)
- self.vsa_api.update_vsa_status(context, vsa_id,
- vsa_api.VsaState.FAILED)
- return
-
- # create user-data record for VC
- storage_data = vsa_utils.generate_user_data(vsa, drives)
-
- instance_type = instance_types.get_instance_type(
- vsa['instance_type_id'])
-
- # now start the VC instance
-
- vc_count = vsa['vc_count']
- LOG.info(_("VSA ID %(vsa_id)d: Start %(vc_count)d instances"),
- locals())
- vc_instances = self.compute_api.create(context,
- instance_type, # vsa['vsa_instance_type'],
- vsa['image_ref'],
- min_count=1,
- max_count=vc_count,
- display_name='vc-' + vsa['display_name'],
- display_description='VC for VSA ' + vsa['display_name'],
- availability_zone=vsa['availability_zone'],
- user_data=storage_data,
- metadata=dict(vsa_id=str(vsa_id)))
-
- self.vsa_api.update_vsa_status(context, vsa_id,
- vsa_api.VsaState.CREATED)
diff --git a/nova/vsa/utils.py b/nova/vsa/utils.py
deleted file mode 100644
index 1de341ac5..000000000
--- a/nova/vsa/utils.py
+++ /dev/null
@@ -1,80 +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.
-
-import base64
-from xml.etree import ElementTree
-
-from nova import flags
-
-FLAGS = flags.FLAGS
-
-
-def generate_user_data(vsa, volumes):
- SubElement = ElementTree.SubElement
-
- e_vsa = ElementTree.Element("vsa")
-
- e_vsa_detail = SubElement(e_vsa, "id")
- e_vsa_detail.text = str(vsa['id'])
- e_vsa_detail = SubElement(e_vsa, "name")
- e_vsa_detail.text = vsa['display_name']
- e_vsa_detail = SubElement(e_vsa, "description")
- e_vsa_detail.text = vsa['display_description']
- e_vsa_detail = SubElement(e_vsa, "vc_count")
- e_vsa_detail.text = str(vsa['vc_count'])
-
- e_vsa_detail = SubElement(e_vsa, "auth_user")
- e_vsa_detail.text = FLAGS.vsa_ec2_user_id
- e_vsa_detail = SubElement(e_vsa, "auth_access_key")
- e_vsa_detail.text = FLAGS.vsa_ec2_access_key
-
- e_volumes = SubElement(e_vsa, "volumes")
- for volume in volumes:
-
- loc = volume['provider_location']
- if loc is None:
- ip = ''
- iscsi_iqn = ''
- iscsi_portal = ''
- else:
- (iscsi_target, _sep, iscsi_iqn) = loc.partition(" ")
- (ip, iscsi_portal) = iscsi_target.split(":", 1)
-
- e_vol = SubElement(e_volumes, "volume")
- e_vol_detail = SubElement(e_vol, "id")
- e_vol_detail.text = str(volume['id'])
- e_vol_detail = SubElement(e_vol, "name")
- e_vol_detail.text = volume['name']
- e_vol_detail = SubElement(e_vol, "display_name")
- e_vol_detail.text = volume['display_name']
- e_vol_detail = SubElement(e_vol, "size_gb")
- e_vol_detail.text = str(volume['size'])
- e_vol_detail = SubElement(e_vol, "status")
- e_vol_detail.text = volume['status']
- e_vol_detail = SubElement(e_vol, "ip")
- e_vol_detail.text = ip
- e_vol_detail = SubElement(e_vol, "iscsi_iqn")
- e_vol_detail.text = iscsi_iqn
- e_vol_detail = SubElement(e_vol, "iscsi_portal")
- e_vol_detail.text = iscsi_portal
- e_vol_detail = SubElement(e_vol, "lun")
- e_vol_detail.text = '0'
- e_vol_detail = SubElement(e_vol, "sn_host")
- e_vol_detail.text = volume['host']
-
- _xml = ElementTree.tostring(e_vsa)
- return base64.b64encode(_xml)