From b814f9fef3efa1bdcb7e03a9161e08721b7bc8c4 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 15 Jul 2011 17:56:27 -0700 Subject: VSA: first cut. merged with 1279 --- nova/api/ec2/__init__.py | 4 + nova/api/ec2/cloud.py | 164 +++++++- nova/api/openstack/contrib/drive_types.py | 147 +++++++ .../openstack/contrib/virtual_storage_arrays.py | 454 +++++++++++++++++++++ nova/api/openstack/contrib/volumes.py | 14 +- 5 files changed, 775 insertions(+), 8 deletions(-) create mode 100644 nova/api/openstack/contrib/drive_types.py create mode 100644 nova/api/openstack/contrib/virtual_storage_arrays.py (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 890d57fe7..ec44c02ef 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -269,6 +269,10 @@ class Authorizer(wsgi.Middleware): 'DescribeImageAttribute': ['all'], 'ModifyImageAttribute': ['projectmanager', 'sysadmin'], 'UpdateImage': ['projectmanager', 'sysadmin'], + 'CreateVsa': ['projectmanager', 'sysadmin'], + 'DeleteVsa': ['projectmanager', 'sysadmin'], + 'DescribeVsas': ['projectmanager', 'sysadmin'], + 'DescribeDriveTypes': ['projectmanager', 'sysadmin'], }, 'AdminController': { # All actions have the same permission: ['none'] (the default) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index acfd1361c..786ceaccc 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -42,6 +42,8 @@ from nova import network from nova import rpc from nova import utils from nova import volume +from nova import vsa +from nova.vsa import drive_types from nova.api.ec2 import ec2utils from nova.compute import instance_types from nova.image import s3 @@ -87,6 +89,7 @@ class CloudController(object): self.compute_api = compute.API( network_api=self.network_api, volume_api=self.volume_api) + self.vsa_api = vsa.API(compute_api=self.compute_api) self.setup() def __str__(self): @@ -727,12 +730,26 @@ class CloudController(object): snapshot_id = None LOG.audit(_("Create volume of %s GB"), size, context=context) + to_vsa_id = kwargs.get('to_vsa_id', None) + if to_vsa_id: + to_vsa_id = ec2utils.ec2_id_to_id(to_vsa_id) + + from_vsa_id = kwargs.get('from_vsa_id', None) + if from_vsa_id: + from_vsa_id = ec2utils.ec2_id_to_id(from_vsa_id) + + if to_vsa_id or from_vsa_id: + LOG.audit(_("Create volume of %s GB associated with VSA "\ + "(to: %d, from: %d)"), + size, to_vsa_id, from_vsa_id, context=context) + volume = self.volume_api.create( context, size=size, snapshot_id=snapshot_id, name=kwargs.get('display_name'), - description=kwargs.get('display_description')) + description=kwargs.get('display_description'), + to_vsa_id=to_vsa_id, from_vsa_id=from_vsa_id) # TODO(vish): Instance should be None at db layer instead of # trying to lazy load, but for now we turn it into # a dict to avoid an error. @@ -786,6 +803,151 @@ class CloudController(object): 'status': volume['attach_status'], 'volumeId': ec2utils.id_to_ec2_id(volume_id, 'vol-%08x')} + def _format_vsa(self, context, p_vsa): + vsa = {} + vsa['vsaId'] = p_vsa['id'] + vsa['status'] = p_vsa['status'] + vsa['availabilityZone'] = p_vsa['availability_zone'] + vsa['createTime'] = p_vsa['created_at'] + vsa['name'] = p_vsa['name'] + vsa['displayName'] = p_vsa['display_name'] + vsa['displayDescription'] = p_vsa['display_description'] + vsa['vcCount'] = p_vsa['vc_count'] + if p_vsa['vsa_instance_type']: + vsa['vcType'] = p_vsa['vsa_instance_type'].get('name', None) + else: + vsa['vcType'] = None + return vsa + + def create_vsa(self, context, **kwargs): + display_name = kwargs.get('display_name') + display_description = kwargs.get('display_description') + vc_count = int(kwargs.get('vc_count', 1)) + instance_type = instance_types.get_instance_type_by_name( + kwargs.get('vc_type', FLAGS.default_vsa_instance_type)) + image_name = kwargs.get('image_name') + availability_zone = kwargs.get('placement', {}).get( + 'AvailabilityZone') + #storage = ast.literal_eval(kwargs.get('storage', '[]')) + storage = kwargs.get('storage', []) + shared = kwargs.get('shared', False) + + vc_type = instance_type['name'] + _storage = str(storage) + LOG.audit(_("Create VSA %(display_name)s vc_count:%(vc_count)d "\ + "vc_type:%(vc_type)s storage:%(_storage)s"), locals()) + + vsa = self.vsa_api.create(context, display_name, display_description, + vc_count, instance_type, image_name, + availability_zone, storage, shared) + return {'vsaSet': [self._format_vsa(context, vsa)]} + + def update_vsa(self, context, vsa_id, **kwargs): + LOG.audit(_("Update VSA %s"), vsa_id) + updatable_fields = ['display_name', 'display_description', 'vc_count'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + vsa_id = ec2utils.ec2_id_to_id(vsa_id) + self.vsa_api.update(context, vsa_id=vsa_id, **changes) + return True + + def delete_vsa(self, context, vsa_id, **kwargs): + LOG.audit(_("Delete VSA %s"), vsa_id) + vsa_id = ec2utils.ec2_id_to_id(vsa_id) + + self.vsa_api.delete(context, vsa_id) + + return True + + def describe_vsas(self, context, vsa_id=None, status=None, + availability_zone=None, **kwargs): +# LOG.debug(_("vsa_id=%s, status=%s, az=%s"), +# (vsa_id, status, availability_zone)) + result = [] + vsas = [] + if vsa_id is not None: + for ec2_id in vsa_id: + internal_id = ec2utils.ec2_id_to_id(ec2_id) + vsa = self.vsa_api.get(context, internal_id) + vsas.append(vsa) + else: + vsas = self.vsa_api.get_all(context) + + if status: + result = [] + for vsa in vsas: + if vsa['status'] in status: + result.append(vsa) + vsas = result + + if availability_zone: + result = [] + for vsa in vsas: + if vsa['availability_zone'] in availability_zone: + result.append(vsa) + vsas = result + + return {'vsaSet': [self._format_vsa(context, vsa) for vsa in vsas]} + + def create_drive_type(self, context, **kwargs): + name = kwargs.get('name') + type = kwargs.get('type') + size_gb = int(kwargs.get('size_gb')) + rpm = kwargs.get('rpm') + capabilities = kwargs.get('capabilities') + visible = kwargs.get('visible', True) + + LOG.audit(_("Create Drive Type %(name)s: %(type)s %(size_gb)d "\ + "%(rpm)s %(capabilities)s %(visible)s"), + locals()) + + rv = drive_types.drive_type_create(context, type, size_gb, rpm, + capabilities, visible, name) + return {'driveTypeSet': [dict(rv)]} + + def update_drive_type(self, context, name, **kwargs): + LOG.audit(_("Update Drive Type %s"), name) + updatable_fields = ['type', + 'size_gb', + 'rpm', + 'capabilities', + 'visible'] + changes = {} + for field in updatable_fields: + if field in kwargs and \ + kwargs[field] is not None and \ + kwargs[field] != '': + changes[field] = kwargs[field] + if changes: + drive_types.drive_type_update(context, name, **changes) + return True + + def rename_drive_type(self, context, name, new_name): + drive_types.drive_type_rename(context, name, new_name) + return True + + def delete_drive_type(self, context, name): + drive_types.drive_type_delete(context, name) + return True + + def describe_drive_types(self, context, names=None, visible=True): + + drives = [] + if names is not None: + for name in names: + drive = drive_types.drive_type_get_by_name(context, name) + if drive['visible'] == visible: + drives.append(drive) + else: + drives = drive_types.drive_type_get_all(context, visible) + + # VP-TODO: Change it later to EC2 compatible func (output) + + return {'driveTypeSet': [dict(drive) for drive in drives]} + def _convert_to_set(self, lst, label): if lst is None or lst == []: return None diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py new file mode 100644 index 000000000..85b3170cb --- /dev/null +++ b/nova/api/openstack/contrib/drive_types.py @@ -0,0 +1,147 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" The Drive Types extension for Virtual Storage Arrays""" + + +from webob import exc + +from nova.vsa import drive_types +from nova import db +from nova import quota +from nova import log as logging +from nova.api.openstack import common +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.api.openstack import wsgi + +LOG = logging.getLogger("nova.api.drive_types") + + +class DriveTypeController(object): + """The Drive Type API controller for the OpenStack API.""" + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "drive_type": [ + "id", + "displayName", + "type", + "size", + "rpm", + "capabilities", + ]}}} + + def _drive_type_view(self, context, drive): + """Maps keys for drive types view.""" + d = {} + + d['id'] = drive['id'] + d['displayName'] = drive['name'] + d['type'] = drive['type'] + d['size'] = drive['size_gb'] + d['rpm'] = drive['rpm'] + d['capabilities'] = drive['capabilities'] + return d + + def index(self, req): + """Returns a list of drive types.""" + + context = req.environ['nova.context'] + drive_types = drive_types.drive_type_get_all(context) + limited_list = common.limited(drive_types, req) + res = [self._drive_type_view(context, drive) for drive in limited_list] + return {'drive_types': res} + + def show(self, req, id): + """Return data about the given drive type.""" + context = req.environ['nova.context'] + + try: + drive = drive_types.drive_type_get(context, id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + return {'drive_type': self._drive_type_view(context, drive)} + + def create(self, req, body): + """Creates a new drive type.""" + context = req.environ['nova.context'] + + if not body: + return faults.Fault(exc.HTTPUnprocessableEntity()) + + drive = body['drive_type'] + + name = drive.get('displayName') + type = drive.get('type') + size = drive.get('size') + rpm = drive.get('rpm') + capabilities = drive.get('capabilities') + + LOG.audit(_("Create drive type %(name)s for "\ + "%(type)s:%(size)s:%(rpm)s"), locals(), context=context) + + new_drive = drive_types.drive_type_create(context, + type=type, + size_gb=size, + rpm=rpm, + capabilities=capabilities, + name=name) + + return {'drive_type': self._drive_type_view(context, new_drive)} + + def delete(self, req, id): + """Deletes a drive type.""" + context = req.environ['nova.context'] + + LOG.audit(_("Delete drive type with id: %s"), id, context=context) + + try: + drive = drive_types.drive_type_get(context, id) + drive_types.drive_type_delete(context, drive['name']) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return exc.HTTPAccepted() + + +class Drive_types(extensions.ExtensionDescriptor): + + def get_name(self): + return "DriveTypes" + + def get_alias(self): + return "zadr-drive_types" + + def get_description(self): + return "Drive Types support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/drive_types/api/v1.1" + + def get_updated(self): + return "2011-06-29T00:00:00+00:00" + + def get_resources(self): + resources = [] + res = extensions.ResourceExtension( + 'zadr-drive_types', + DriveTypeController()) + + resources.append(res) + return resources diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py new file mode 100644 index 000000000..eca2d68dd --- /dev/null +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -0,0 +1,454 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Zadara Storage Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" The virtul storage array extension""" + + +from webob import exc + +from nova import vsa +from nova import volume +from nova import db +from nova import quota +from nova import exception +from nova import log as logging +from nova.api.openstack import common +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.api.openstack import wsgi +from nova.api.openstack.contrib import volumes +from nova.compute import instance_types + +from nova import flags +FLAGS = flags.FLAGS + +LOG = logging.getLogger("nova.api.vsa") + + +class VsaController(object): + """The Virtual Storage Array API controller for the OpenStack API.""" + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "vsa": [ + "id", + "name", + "displayName", + "displayDescription", + "createTime", + "status", + "vcType", + "vcCount", + "driveCount", + ]}}} + + def __init__(self): + self.vsa_api = vsa.API() + super(VsaController, self).__init__() + + def _vsa_view(self, context, vsa, details=False): + """Map keys for vsa summary/detailed view.""" + d = {} + + d['id'] = vsa['id'] + d['name'] = vsa['name'] + d['displayName'] = vsa['display_name'] + d['displayDescription'] = vsa['display_description'] + + d['createTime'] = vsa['created_at'] + d['status'] = vsa['status'] + + if vsa['vsa_instance_type']: + d['vcType'] = vsa['vsa_instance_type'].get('name', None) + else: + d['vcType'] = None + + d['vcCount'] = vsa['vc_count'] + d['driveCount'] = vsa['vol_count'] + + return d + + def _items(self, req, details): + """Return summary or detailed list of VSAs.""" + context = req.environ['nova.context'] + vsas = self.vsa_api.get_all(context) + limited_list = common.limited(vsas, req) + res = [self._vsa_view(context, vsa, details) for vsa in limited_list] + return {'vsaSet': res} + + def index(self, req): + """Return a short list of VSAs.""" + return self._items(req, details=False) + + def detail(self, req): + """Return a detailed list of VSAs.""" + return self._items(req, details=True) + + def show(self, req, id): + """Return data about the given VSA.""" + context = req.environ['nova.context'] + + try: + vsa = self.vsa_api.get(context, vsa_id=id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + return {'vsa': self._vsa_view(context, vsa, details=True)} + + def create(self, req, body): + """Create a new VSA.""" + context = req.environ['nova.context'] + + if not body: + return faults.Fault(exc.HTTPUnprocessableEntity()) + + vsa = body['vsa'] + + display_name = vsa.get('displayName') + display_description = vsa.get('displayDescription') + storage = vsa.get('storage') + shared = vsa.get('shared') + vc_type = vsa.get('vcType', FLAGS.default_vsa_instance_type) + availability_zone = vsa.get('placement', {}).get('AvailabilityZone') + + try: + instance_type = instance_types.get_instance_type_by_name(vc_type) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + LOG.audit(_("Create VSA %(display_name)s of type %(vc_type)s"), + locals(), context=context) + + result = self.vsa_api.create(context, + display_name=display_name, + display_description=display_description, + storage=storage, + shared=shared, + instance_type=instance_type, + availability_zone=availability_zone) + + return {'vsa': self._vsa_view(context, result, details=True)} + + def delete(self, req, id): + """Delete a VSA.""" + context = req.environ['nova.context'] + + LOG.audit(_("Delete VSA with id: %s"), id, context=context) + + try: + self.vsa_api.delete(context, vsa_id=id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return exc.HTTPAccepted() + + +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 + + """ + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "volume": [ + "id", + "name", + "status", + "size", + "availabilityZone", + "createdAt", + "displayName", + "displayDescription", + "vsaId", + ]}}} + + def __init__(self): + # self.compute_api = compute.API() + # self.vsa_api = vsa.API() + self.volume_api = volume.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'] = vol[self.direction] + return d + + def _check_volume_ownership(self, context, vsa_id, id): + obj = self.object + try: + volume_ref = self.volume_api.get(context, volume_id=id) + except exception.NotFound: + LOG.error(_("%(obj)s with ID %(id)s not found"), locals()) + raise + + own_vsa_id = volume_ref[self.direction] + if own_vsa_id != int(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'] + + vols = self.volume_api.get_all_by_vsa(context, vsa_id, + self.direction.split('_')[0]) + 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'] + + if not body: + return faults.Fault(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) + + new_volume = self.volume_api.create(context, size, None, + vol.get('displayName'), + vol.get('displayDescription'), + from_vsa_id=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'] + + try: + self._check_volume_ownership(context, vsa_id, id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + except exception.Invalid: + return faults.Fault(exc.HTTPBadRequest()) + + vol = body[self.object] + updatable_fields = ['display_name', + 'display_description', + 'status', + 'provider_location', + 'provider_auth'] + changes = {} + for field in updatable_fields: + if field in vol: + changes[field] = vol[field] + + obj = self.object + LOG.audit(_("Update %(obj)s with id: %(id)s, changes: %(changes)s"), + locals(), context=context) + + try: + self.volume_api.update(context, volume_id=id, fields=changes) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return exc.HTTPAccepted() + + def delete(self, req, vsa_id, id): + """Delete a volume.""" + context = req.environ['nova.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: + return faults.Fault(exc.HTTPNotFound()) + except exception.Invalid: + return faults.Fault(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'] + + LOG.audit(_("Show. vsa_id=%(vsa_id)s, id=%(id)s"), locals()) + + try: + self._check_volume_ownership(context, vsa_id, id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + except exception.Invalid: + return faults.Fault(exc.HTTPBadRequest()) + + return super(VsaVolumeDriveController, self).show(req, id) + + +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__() + + +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""" + return faults.Fault(exc.HTTPBadRequest()) + + def update(self, req, vsa_id, id, body): + """Update a drive. Should be done through VSA APIs""" + return faults.Fault(exc.HTTPBadRequest()) + + +class VsaVPoolController(object): + """The vPool VSA API controller for the OpenStack API.""" + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "vpool": [ + "id", + "vsaId", + "name", + "displayName", + "displayDescription", + "driveCount", + "driveIds", + "protection", + "stripeSize", + "stripeWidth", + "createTime", + "status", + ]}}} + + def __init__(self): + self.vsa_api = vsa.API() + super(VsaVPoolController, self).__init__() + + 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.""" + return faults.Fault(exc.HTTPBadRequest()) + + def update(self, req, vsa_id, id, body): + """Update vPool parameters.""" + return faults.Fault(exc.HTTPBadRequest()) + + def delete(self, req, vsa_id, id): + """Delete a vPool.""" + return faults.Fault(exc.HTTPBadRequest()) + + def show(self, req, vsa_id, id): + """Return data about the given vPool.""" + return faults.Fault(exc.HTTPBadRequest()) + + +class Virtual_storage_arrays(extensions.ExtensionDescriptor): + + def get_name(self): + return "VSAs" + + def get_alias(self): + return "zadr-vsa" + + def get_description(self): + return "Virtual Storage Arrays support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/vsa/api/v1.1" + + def get_updated(self): + return "2011-06-29T00: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'}) + 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) + + return resources diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py index e5e2c5b50..3c3d40c0f 100644 --- a/nova/api/openstack/contrib/volumes.py +++ b/nova/api/openstack/contrib/volumes.py @@ -33,17 +33,17 @@ LOG = logging.getLogger("nova.api.volumes") FLAGS = flags.FLAGS -def _translate_volume_detail_view(context, vol): +def translate_volume_detail_view(context, vol): """Maps keys for volumes details view.""" - d = _translate_volume_summary_view(context, vol) + d = translate_volume_summary_view(context, vol) # No additional data / lookups at the moment return d -def _translate_volume_summary_view(context, vol): +def translate_volume_summary_view(context, vol): """Maps keys for volumes summary view.""" d = {} @@ -92,7 +92,7 @@ class VolumeController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'volume': _translate_volume_detail_view(context, vol)} + return {'volume': translate_volume_detail_view(context, vol)} def delete(self, req, id): """Delete a volume.""" @@ -108,11 +108,11 @@ class VolumeController(object): def index(self, req): """Returns a summary list of volumes.""" - return self._items(req, entity_maker=_translate_volume_summary_view) + return self._items(req, entity_maker=translate_volume_summary_view) def detail(self, req): """Returns a detailed list of volumes.""" - return self._items(req, entity_maker=_translate_volume_detail_view) + return self._items(req, entity_maker=translate_volume_detail_view) def _items(self, req, entity_maker): """Returns a list of volumes, transformed through entity_maker.""" @@ -140,7 +140,7 @@ class VolumeController(object): # Work around problem that instance is lazy-loaded... new_volume['instance'] = None - retval = _translate_volume_detail_view(context, new_volume) + retval = translate_volume_detail_view(context, new_volume) return {'volume': retval} -- cgit From 15bbaf8bbdd48231f9ce98e4d8867b0477b44645 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Mon, 18 Jul 2011 09:57:31 -0700 Subject: localization changes. Removed vsa params from volume cloud API. Alex changes --- nova/api/ec2/cloud.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 786ceaccc..e31b755de 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -730,26 +730,12 @@ class CloudController(object): snapshot_id = None LOG.audit(_("Create volume of %s GB"), size, context=context) - to_vsa_id = kwargs.get('to_vsa_id', None) - if to_vsa_id: - to_vsa_id = ec2utils.ec2_id_to_id(to_vsa_id) - - from_vsa_id = kwargs.get('from_vsa_id', None) - if from_vsa_id: - from_vsa_id = ec2utils.ec2_id_to_id(from_vsa_id) - - if to_vsa_id or from_vsa_id: - LOG.audit(_("Create volume of %s GB associated with VSA "\ - "(to: %d, from: %d)"), - size, to_vsa_id, from_vsa_id, context=context) - volume = self.volume_api.create( context, size=size, snapshot_id=snapshot_id, name=kwargs.get('display_name'), - description=kwargs.get('display_description'), - to_vsa_id=to_vsa_id, from_vsa_id=from_vsa_id) + description=kwargs.get('display_description')) # TODO(vish): Instance should be None at db layer instead of # trying to lazy load, but for now we turn it into # a dict to avoid an error. @@ -864,8 +850,7 @@ class CloudController(object): def describe_vsas(self, context, vsa_id=None, status=None, availability_zone=None, **kwargs): -# LOG.debug(_("vsa_id=%s, status=%s, az=%s"), -# (vsa_id, status, availability_zone)) + LOG.audit(_("Describe VSAs")) result = [] vsas = [] if vsa_id is not None: -- cgit From 9e74803d5eb8a70ba829ac0569f1cd6cd372a6f2 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 22 Jul 2011 15:14:29 -0700 Subject: Reverted volume driver part --- nova/api/ec2/cloud.py | 19 ++++-- nova/api/openstack/contrib/drive_types.py | 55 ++++++++-------- .../openstack/contrib/virtual_storage_arrays.py | 77 +++++++++++----------- 3 files changed, 78 insertions(+), 73 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e31b755de..7d0ce360f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -889,12 +889,15 @@ class CloudController(object): "%(rpm)s %(capabilities)s %(visible)s"), locals()) - rv = drive_types.drive_type_create(context, type, size_gb, rpm, - capabilities, visible, name) + rv = drive_types.create(context, type, size_gb, rpm, + capabilities, visible, name) return {'driveTypeSet': [dict(rv)]} def update_drive_type(self, context, name, **kwargs): LOG.audit(_("Update Drive Type %s"), name) + + dtype = drive_types.get_by_name(context, name) + updatable_fields = ['type', 'size_gb', 'rpm', @@ -906,16 +909,18 @@ class CloudController(object): kwargs[field] is not None and \ kwargs[field] != '': changes[field] = kwargs[field] + if changes: - drive_types.drive_type_update(context, name, **changes) + drive_types.update(context, dtype['id'], **changes) return True def rename_drive_type(self, context, name, new_name): - drive_types.drive_type_rename(context, name, new_name) + drive_types.rename(context, name, new_name) return True def delete_drive_type(self, context, name): - drive_types.drive_type_delete(context, name) + dtype = drive_types.get_by_name(context, name) + drive_types.delete(context, dtype['id']) return True def describe_drive_types(self, context, names=None, visible=True): @@ -923,11 +928,11 @@ class CloudController(object): drives = [] if names is not None: for name in names: - drive = drive_types.drive_type_get_by_name(context, name) + drive = drive_types.get_by_name(context, name) if drive['visible'] == visible: drives.append(drive) else: - drives = drive_types.drive_type_get_all(context, visible) + drives = drive_types.get_all(context, visible) # VP-TODO: Change it later to EC2 compatible func (output) diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py index 85b3170cb..590eaaec0 100644 --- a/nova/api/openstack/contrib/drive_types.py +++ b/nova/api/openstack/contrib/drive_types.py @@ -21,6 +21,7 @@ from webob import exc from nova.vsa import drive_types +from nova import exception from nova import db from nova import quota from nova import log as logging @@ -32,6 +33,19 @@ from nova.api.openstack import wsgi LOG = logging.getLogger("nova.api.drive_types") +def _drive_type_view(drive): + """Maps keys for drive types view.""" + d = {} + + d['id'] = drive['id'] + d['displayName'] = drive['name'] + d['type'] = drive['type'] + d['size'] = drive['size_gb'] + d['rpm'] = drive['rpm'] + d['capabilities'] = drive['capabilities'] + return d + + class DriveTypeController(object): """The Drive Type API controller for the OpenStack API.""" @@ -47,25 +61,13 @@ class DriveTypeController(object): "capabilities", ]}}} - def _drive_type_view(self, context, drive): - """Maps keys for drive types view.""" - d = {} - - d['id'] = drive['id'] - d['displayName'] = drive['name'] - d['type'] = drive['type'] - d['size'] = drive['size_gb'] - d['rpm'] = drive['rpm'] - d['capabilities'] = drive['capabilities'] - return d - def index(self, req): """Returns a list of drive types.""" context = req.environ['nova.context'] - drive_types = drive_types.drive_type_get_all(context) - limited_list = common.limited(drive_types, req) - res = [self._drive_type_view(context, drive) for drive in limited_list] + dtypes = drive_types.get_all(context) + limited_list = common.limited(dtypes, req) + res = [_drive_type_view(drive) for drive in limited_list] return {'drive_types': res} def show(self, req, id): @@ -73,11 +75,11 @@ class DriveTypeController(object): context = req.environ['nova.context'] try: - drive = drive_types.drive_type_get(context, id) + drive = drive_types.get(context, id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'drive_type': self._drive_type_view(context, drive)} + return {'drive_type': _drive_type_view(drive)} def create(self, req, body): """Creates a new drive type.""" @@ -97,14 +99,14 @@ class DriveTypeController(object): LOG.audit(_("Create drive type %(name)s for "\ "%(type)s:%(size)s:%(rpm)s"), locals(), context=context) - new_drive = drive_types.drive_type_create(context, - type=type, - size_gb=size, - rpm=rpm, - capabilities=capabilities, - name=name) + new_drive = drive_types.create(context, + type=type, + size_gb=size, + rpm=rpm, + capabilities=capabilities, + name=name) - return {'drive_type': self._drive_type_view(context, new_drive)} + return {'drive_type': _drive_type_view(new_drive)} def delete(self, req, id): """Deletes a drive type.""" @@ -113,11 +115,10 @@ class DriveTypeController(object): LOG.audit(_("Delete drive type with id: %s"), id, context=context) try: - drive = drive_types.drive_type_get(context, id) - drive_types.drive_type_delete(context, drive['name']) + drive_types.delete(context, id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + # return exc.HTTPAccepted() class Drive_types(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index eca2d68dd..3c1362f0c 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -39,6 +39,29 @@ FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.vsa") +def _vsa_view(context, vsa, details=False): + """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'] = None + + d['vcCount'] = vsa.get('vc_count') + d['driveCount'] = vsa.get('vol_count') + + return d + + class VsaController(object): """The Virtual Storage Array API controller for the OpenStack API.""" @@ -61,34 +84,12 @@ class VsaController(object): self.vsa_api = vsa.API() super(VsaController, self).__init__() - def _vsa_view(self, context, vsa, details=False): - """Map keys for vsa summary/detailed view.""" - d = {} - - d['id'] = vsa['id'] - d['name'] = vsa['name'] - d['displayName'] = vsa['display_name'] - d['displayDescription'] = vsa['display_description'] - - d['createTime'] = vsa['created_at'] - d['status'] = vsa['status'] - - if vsa['vsa_instance_type']: - d['vcType'] = vsa['vsa_instance_type'].get('name', None) - else: - d['vcType'] = None - - d['vcCount'] = vsa['vc_count'] - d['driveCount'] = vsa['vol_count'] - - return d - def _items(self, req, details): """Return summary or detailed list of VSAs.""" context = req.environ['nova.context'] vsas = self.vsa_api.get_all(context) limited_list = common.limited(vsas, req) - res = [self._vsa_view(context, vsa, details) for vsa in limited_list] + res = [_vsa_view(context, vsa, details) for vsa in limited_list] return {'vsaSet': res} def index(self, req): @@ -108,24 +109,20 @@ class VsaController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'vsa': self._vsa_view(context, vsa, details=True)} + return {'vsa': _vsa_view(context, vsa, details=True)} def create(self, req, body): """Create a new VSA.""" context = req.environ['nova.context'] - if not body: + if not body or 'vsa' not in body: + LOG.debug(_("No body provided"), context=context) return faults.Fault(exc.HTTPUnprocessableEntity()) vsa = body['vsa'] display_name = vsa.get('displayName') - display_description = vsa.get('displayDescription') - storage = vsa.get('storage') - shared = vsa.get('shared') vc_type = vsa.get('vcType', FLAGS.default_vsa_instance_type) - availability_zone = vsa.get('placement', {}).get('AvailabilityZone') - try: instance_type = instance_types.get_instance_type_by_name(vc_type) except exception.NotFound: @@ -134,15 +131,17 @@ class VsaController(object): LOG.audit(_("Create VSA %(display_name)s of type %(vc_type)s"), locals(), context=context) - result = self.vsa_api.create(context, - display_name=display_name, - display_description=display_description, - storage=storage, - shared=shared, - instance_type=instance_type, - availability_zone=availability_zone) + 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.get('placement', {}).\ + get('AvailabilityZone')) - return {'vsa': self._vsa_view(context, result, details=True)} + result = self.vsa_api.create(context, **args) + + return {'vsa': _vsa_view(context, result, details=True)} def delete(self, req, id): """Delete a VSA.""" @@ -154,7 +153,7 @@ class VsaController(object): self.vsa_api.delete(context, vsa_id=id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return exc.HTTPAccepted() + # return exc.HTTPAccepted() class VsaVolumeDriveController(volumes.VolumeController): -- cgit From 61781dae931ced36db0f2735da474d0bd38a53cf Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 22 Jul 2011 20:25:32 -0700 Subject: more unittest changes --- nova/api/openstack/contrib/virtual_storage_arrays.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index 3c1362f0c..6139b494e 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -353,6 +353,10 @@ class VsaDriveController(VsaVolumeDriveController): """Update a drive. Should be done through VSA APIs""" return faults.Fault(exc.HTTPBadRequest()) + def delete(self, req, vsa_id, id): + """Delete a volume. Should be done through VSA APIs""" + return faults.Fault(exc.HTTPBadRequest()) + class VsaVPoolController(object): """The vPool VSA API controller for the OpenStack API.""" -- cgit From c500eac4589e9cb22e5e71b900164a151290ec03 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Mon, 25 Jul 2011 16:26:23 -0700 Subject: some cleanup. VSA flag status changes. returned some files --- nova/api/ec2/cloud.py | 1 - nova/api/openstack/contrib/drive_types.py | 2 +- nova/api/openstack/contrib/virtual_storage_arrays.py | 19 +++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 56a5850f6..6fc74c92a 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -902,7 +902,6 @@ class CloudController(object): image_name = kwargs.get('image_name') availability_zone = kwargs.get('placement', {}).get( 'AvailabilityZone') - #storage = ast.literal_eval(kwargs.get('storage', '[]')) storage = kwargs.get('storage', []) shared = kwargs.get('shared', False) diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py index 590eaaec0..6454fd81f 100644 --- a/nova/api/openstack/contrib/drive_types.py +++ b/nova/api/openstack/contrib/drive_types.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -118,7 +119,6 @@ class DriveTypeController(object): drive_types.delete(context, id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - # return exc.HTTPAccepted() class Drive_types(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index 6139b494e..68a00fd7d 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2011 Zadara Storage Inc. +# Copyright (c) 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -153,7 +154,6 @@ class VsaController(object): self.vsa_api.delete(context, vsa_id=id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - # return exc.HTTPAccepted() class VsaVolumeDriveController(volumes.VolumeController): @@ -193,6 +193,7 @@ class VsaVolumeDriveController(volumes.VolumeController): d = translation(context, vol) d['vsaId'] = vol[self.direction] + d['name'] = vol['name'] return d def _check_volume_ownership(self, context, vsa_id, id): @@ -265,15 +266,17 @@ class VsaVolumeDriveController(volumes.VolumeController): return faults.Fault(exc.HTTPBadRequest()) vol = body[self.object] - updatable_fields = ['display_name', - 'display_description', - 'status', - 'provider_location', - 'provider_auth'] + updatable_fields = [{'displayName': 'display_name'}, + {'displayDescription': 'display_description'}, + {'status': 'status'}, + {'providerLocation': 'provider_location'}, + {'providerAuth': 'provider_auth'}] changes = {} for field in updatable_fields: - if field in vol: - changes[field] = vol[field] + 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"), -- cgit From b4159d95c32382d124c3f3f0a49f8ad9f2d41036 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 28 Jul 2011 00:27:16 -0700 Subject: some minor cosmetic work. addressed some dead code section --- nova/api/openstack/contrib/drive_types.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py index 6454fd81f..f2cbd3715 100644 --- a/nova/api/openstack/contrib/drive_types.py +++ b/nova/api/openstack/contrib/drive_types.py @@ -18,18 +18,14 @@ """ The Drive Types extension for Virtual Storage Arrays""" - from webob import exc from nova.vsa import drive_types from nova import exception -from nova import db -from nova import quota from nova import log as logging from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import faults -from nova.api.openstack import wsgi LOG = logging.getLogger("nova.api.drive_types") -- cgit From 16cbba0838e9a2ac712b91b103dc794b0edebd00 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 28 Jul 2011 00:45:16 -0700 Subject: more commented code removed --- nova/api/openstack/contrib/virtual_storage_arrays.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index 68a00fd7d..842573f8a 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -180,8 +180,6 @@ class VsaVolumeDriveController(volumes.VolumeController): ]}}} def __init__(self): - # self.compute_api = compute.API() - # self.vsa_api = vsa.API() self.volume_api = volume.API() super(VsaVolumeDriveController, self).__init__() -- cgit From f4359a7789ae96a36aaab8f53aa3234d13b1725a Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 28 Jul 2011 15:54:02 -0700 Subject: returned vsa_manager, nova-manage arg and print changes --- nova/api/ec2/cloud.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index ca1fef51f..0a0644351 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -965,6 +965,10 @@ class CloudController(object): vsa['vcType'] = p_vsa['vsa_instance_type'].get('name', None) else: vsa['vcType'] = None + + vols = self.volume_api.get_all_by_vsa(context, p_vsa['id'], "to") + vsa['volCount'] = 0 if vols is None else len(vols) + return vsa def create_vsa(self, context, **kwargs): -- cgit From bd39829cc1908cb5ead899c9659a5c516b073a4f Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 9 Aug 2011 16:55:51 -0700 Subject: merge with nova-1411. fixed --- nova/api/ec2/cloud.py | 2 +- nova/api/openstack/contrib/floating_ips.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 9094f6b56..ac0ff713b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1149,7 +1149,7 @@ class CloudController(object): return {'driveTypeSet': [dict(drive) for drive in drives]} @staticmethod - def _convert_to_set(self, lst, label): + def _convert_to_set(lst, label): if lst is None or lst == []: return None if not isinstance(lst, list): diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 52c9c6cf9..2aba1068a 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -102,7 +102,7 @@ class FloatingIPController(object): def delete(self, req, id): context = req.environ['nova.context'] ip = self.network_api.get_floating_ip(context, id) - + if 'fixed_ip' in ip: try: self.disassociate(req, id, '') -- cgit From b66ea57ae10bac1656e11663e273837dfae67814 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 12 Aug 2011 12:51:54 -0700 Subject: removed VSA/drive_type code from EC2 cloud. changed nova-manage not to use cloud APIs --- nova/api/ec2/__init__.py | 4 -- nova/api/ec2/cloud.py | 153 ----------------------------------------------- 2 files changed, 157 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 35c809547..8b6e47cfb 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -268,10 +268,6 @@ class Authorizer(wsgi.Middleware): 'StartInstances': ['projectmanager', 'sysadmin'], 'StopInstances': ['projectmanager', 'sysadmin'], 'DeleteVolume': ['projectmanager', 'sysadmin'], - 'CreateVsa': ['projectmanager', 'sysadmin'], - 'DeleteVsa': ['projectmanager', 'sysadmin'], - 'DescribeVsas': ['projectmanager', 'sysadmin'], - 'DescribeDriveTypes': ['projectmanager', 'sysadmin'], 'DescribeImages': ['all'], 'DeregisterImage': ['projectmanager', 'sysadmin'], 'RegisterImage': ['projectmanager', 'sysadmin'], diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index ac0ff713b..87bba58c3 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -45,8 +45,6 @@ from nova import network from nova import rpc from nova import utils from nova import volume -from nova import vsa -from nova.vsa import drive_types from nova.api.ec2 import ec2utils from nova.compute import instance_types from nova.image import s3 @@ -184,7 +182,6 @@ class CloudController(object): self.compute_api = compute.API( network_api=self.network_api, volume_api=self.volume_api) - self.vsa_api = vsa.API(compute_api=self.compute_api) self.setup() def __str__(self): @@ -998,156 +995,6 @@ class CloudController(object): 'status': volume['attach_status'], 'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)} - def _format_vsa(self, context, p_vsa): - vsa = {} - vsa['vsaId'] = p_vsa['id'] - vsa['status'] = p_vsa['status'] - vsa['availabilityZone'] = p_vsa['availability_zone'] - vsa['createTime'] = p_vsa['created_at'] - vsa['name'] = p_vsa['name'] - vsa['displayName'] = p_vsa['display_name'] - vsa['displayDescription'] = p_vsa['display_description'] - vsa['vcCount'] = p_vsa['vc_count'] - if p_vsa['vsa_instance_type']: - vsa['vcType'] = p_vsa['vsa_instance_type'].get('name', None) - else: - vsa['vcType'] = None - - vols = self.volume_api.get_all_by_vsa(context, p_vsa['id'], "to") - vsa['volCount'] = 0 if vols is None else len(vols) - - return vsa - - def create_vsa(self, context, **kwargs): - display_name = kwargs.get('display_name') - display_description = kwargs.get('display_description') - vc_count = int(kwargs.get('vc_count', 1)) - instance_type = instance_types.get_instance_type_by_name( - kwargs.get('vc_type', FLAGS.default_vsa_instance_type)) - image_name = kwargs.get('image_name') - availability_zone = kwargs.get('placement', {}).get( - 'AvailabilityZone') - storage = kwargs.get('storage', []) - shared = kwargs.get('shared', False) - - vc_type = instance_type['name'] - _storage = str(storage) - LOG.audit(_("Create VSA %(display_name)s vc_count:%(vc_count)d "\ - "vc_type:%(vc_type)s storage:%(_storage)s"), locals()) - - vsa = self.vsa_api.create(context, display_name, display_description, - vc_count, instance_type, image_name, - availability_zone, storage, shared) - return {'vsaSet': [self._format_vsa(context, vsa)]} - - def update_vsa(self, context, vsa_id, **kwargs): - LOG.audit(_("Update VSA %s"), vsa_id) - updatable_fields = ['display_name', 'display_description', 'vc_count'] - changes = {} - for field in updatable_fields: - if field in kwargs: - changes[field] = kwargs[field] - if changes: - vsa_id = ec2utils.ec2_id_to_id(vsa_id) - self.vsa_api.update(context, vsa_id=vsa_id, **changes) - return True - - def delete_vsa(self, context, vsa_id, **kwargs): - LOG.audit(_("Delete VSA %s"), vsa_id) - vsa_id = ec2utils.ec2_id_to_id(vsa_id) - - self.vsa_api.delete(context, vsa_id) - - return True - - def describe_vsas(self, context, vsa_id=None, status=None, - availability_zone=None, **kwargs): - LOG.audit(_("Describe VSAs")) - result = [] - vsas = [] - if vsa_id is not None: - for ec2_id in vsa_id: - internal_id = ec2utils.ec2_id_to_id(ec2_id) - vsa = self.vsa_api.get(context, internal_id) - vsas.append(vsa) - else: - vsas = self.vsa_api.get_all(context) - - if status: - result = [] - for vsa in vsas: - if vsa['status'] in status: - result.append(vsa) - vsas = result - - if availability_zone: - result = [] - for vsa in vsas: - if vsa['availability_zone'] in availability_zone: - result.append(vsa) - vsas = result - - return {'vsaSet': [self._format_vsa(context, vsa) for vsa in vsas]} - - def create_drive_type(self, context, **kwargs): - name = kwargs.get('name') - type = kwargs.get('type') - size_gb = int(kwargs.get('size_gb')) - rpm = kwargs.get('rpm') - capabilities = kwargs.get('capabilities') - visible = kwargs.get('visible', True) - - LOG.audit(_("Create Drive Type %(name)s: %(type)s %(size_gb)d "\ - "%(rpm)s %(capabilities)s %(visible)s"), - locals()) - - rv = drive_types.create(context, type, size_gb, rpm, - capabilities, visible, name) - return {'driveTypeSet': [dict(rv)]} - - def update_drive_type(self, context, name, **kwargs): - LOG.audit(_("Update Drive Type %s"), name) - - dtype = drive_types.get_by_name(context, name) - - updatable_fields = ['type', - 'size_gb', - 'rpm', - 'capabilities', - 'visible'] - changes = {} - for field in updatable_fields: - if field in kwargs and \ - kwargs[field] is not None and \ - kwargs[field] != '': - changes[field] = kwargs[field] - - if changes: - drive_types.update(context, dtype['id'], **changes) - return True - - def rename_drive_type(self, context, name, new_name): - drive_types.rename(context, name, new_name) - return True - - def delete_drive_type(self, context, name): - dtype = drive_types.get_by_name(context, name) - drive_types.delete(context, dtype['id']) - return True - - def describe_drive_types(self, context, names=None, visible=True): - - drives = [] - if names is not None: - for name in names: - drive = drive_types.get_by_name(context, name) - if drive['visible'] == visible: - drives.append(drive) - else: - drives = drive_types.get_all(context, visible) - # (VP-TMP): Change to EC2 compliant output later - return {'driveTypeSet': [dict(drive) for drive in drives]} - @staticmethod def _convert_to_set(lst, label): if lst is None or lst == []: -- cgit From cabf9cc8f29ad8c99971c434516e1b911f07f32f Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 17 Aug 2011 16:27:12 -0700 Subject: nova-manage VSA print & forced update_cap changes; fixed bug with report capabilities; added IP address to VSA APIs; added instances to APIs --- .../openstack/contrib/virtual_storage_arrays.py | 107 +++++++++++++++++++-- 1 file changed, 100 insertions(+), 7 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index 842573f8a..d6c4a5ef4 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -23,6 +23,7 @@ from webob import exc from nova import vsa from nova import volume +from nova import compute from nova import db from nova import quota from nova import exception @@ -31,6 +32,7 @@ from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import faults from nova.api.openstack import wsgi +from nova.api.openstack import servers from nova.api.openstack.contrib import volumes from nova.compute import instance_types @@ -40,7 +42,7 @@ FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api.vsa") -def _vsa_view(context, vsa, details=False): +def _vsa_view(context, vsa, details=False, instances=None): """Map keys for vsa summary/detailed view.""" d = {} @@ -55,11 +57,27 @@ def _vsa_view(context, vsa, details=False): if 'vsa_instance_type' in vsa: d['vcType'] = vsa['vsa_instance_type'].get('name', None) else: - d['vcType'] = None + 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 @@ -79,10 +97,12 @@ class VsaController(object): "vcType", "vcCount", "driveCount", + "ipAddress", ]}}} def __init__(self): self.vsa_api = vsa.API() + self.compute_api = compute.API() super(VsaController, self).__init__() def _items(self, req, details): @@ -90,8 +110,13 @@ class VsaController(object): context = req.environ['nova.context'] vsas = self.vsa_api.get_all(context) limited_list = common.limited(vsas, req) - res = [_vsa_view(context, vsa, details) for vsa in limited_list] - return {'vsaSet': res} + + vsa_list = [] + for vsa in limited_list: + instances = self.compute_api.get_all(context, + search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) + vsa_list.append(_vsa_view(context, vsa, details, instances)) + return {'vsaSet': vsa_list} def index(self, req): """Return a short list of VSAs.""" @@ -110,7 +135,10 @@ class VsaController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'vsa': _vsa_view(context, vsa, details=True)} + instances = self.compute_api.get_all(context, + search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) + + return {'vsa': _vsa_view(context, vsa, True, instances)} def create(self, req, body): """Create a new VSA.""" @@ -140,9 +168,12 @@ class VsaController(object): availability_zone=vsa.get('placement', {}).\ get('AvailabilityZone')) - result = self.vsa_api.create(context, **args) + vsa = self.vsa_api.create(context, **args) + + instances = self.compute_api.get_all(context, + search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) - return {'vsa': _vsa_view(context, result, details=True)} + return {'vsa': _vsa_view(context, vsa, True, instances)} def delete(self, req, id): """Delete a VSA.""" @@ -405,6 +436,61 @@ class VsaVPoolController(object): return faults.Fault(exc.HTTPBadRequest()) +class VsaVCController(servers.ControllerV11): + """The VSA Virtual Controller API controller for the OpenStack API.""" + + def __init__(self): + self.vsa_api = vsa.API() + self.compute_api = compute.API() + self.vsa_id = None # VP-TODO: 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) + + 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: 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.""" + return faults.Fault(exc.HTTPBadRequest()) + + def update(self, req, vsa_id, id, body): + """Update VSA instance.""" + return faults.Fault(exc.HTTPBadRequest()) + + def delete(self, req, vsa_id, id): + """Delete VSA instance.""" + return faults.Fault(exc.HTTPBadRequest()) + + 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): def get_name(self): @@ -455,4 +541,11 @@ class Virtual_storage_arrays(extensions.ExtensionDescriptor): 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 -- cgit From 48cd9689de31e408c792052747f714a9dbe1f8f7 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 15:51:29 -0700 Subject: added virtio flag; associate address for VSA; cosmetic changes. Prior to volume_types merge --- nova/api/openstack/contrib/drive_types.py | 1 - .../openstack/contrib/virtual_storage_arrays.py | 49 +++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py index f2cbd3715..1aa65374f 100644 --- a/nova/api/openstack/contrib/drive_types.py +++ b/nova/api/openstack/contrib/drive_types.py @@ -2,7 +2,6 @@ # Copyright (c) 2011 Zadara Storage Inc. # Copyright (c) 2011 OpenStack LLC. -# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index d6c4a5ef4..81dbc9e1f 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -2,7 +2,6 @@ # Copyright (c) 2011 Zadara Storage Inc. # Copyright (c) 2011 OpenStack LLC. -# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -24,6 +23,7 @@ from webob import exc from nova import vsa from nova import volume from nova import compute +from nova import network from nova import db from nova import quota from nova import exception @@ -103,6 +103,7 @@ class VsaController(object): def __init__(self): self.vsa_api = vsa.API() self.compute_api = compute.API() + self.network_api = network.API() super(VsaController, self).__init__() def _items(self, req, details): @@ -186,6 +187,48 @@ class VsaController(object): except exception.NotFound: return faults.Fault(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'] + + 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.compute_api.get_all(context, + search_opts={'metadata': dict(vsa_id=str(id))}) + + if instances is None or len(instances)==0: + return faults.Fault(exc.HTTPNotFound()) + + for instance in instances: + self.network_api.allocate_for_instance(context, instance, vpn=False) + return + + except exception.NotFound: + return faults.Fault(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'] + + if body is None: + ip = 'auto' + else: + ip = body.get('ipAddress', 'auto') + + LOG.audit(_("Disassociate address from VSA %(id)s"), + locals(), context=context) + class VsaVolumeDriveController(volumes.VolumeController): """The base class for VSA volumes & drives. @@ -515,7 +558,9 @@ class Virtual_storage_arrays(extensions.ExtensionDescriptor): VsaController(), collection_actions={'detail': 'GET'}, member_actions={'add_capacity': 'POST', - 'remove_capacity': 'POST'}) + 'remove_capacity': 'POST', + 'associate_address': 'POST', + 'disassociate_address': 'POST'}) resources.append(res) res = extensions.ResourceExtension('volumes', -- cgit From 4834b920e3186712ab56e65a88c2e8c838d16f9c Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 25 Aug 2011 18:38:35 -0700 Subject: VSA code redesign. Drive types completely replaced by Volume types --- nova/api/openstack/contrib/drive_types.py | 143 --------------------- .../openstack/contrib/virtual_storage_arrays.py | 54 ++++---- 2 files changed, 32 insertions(+), 165 deletions(-) delete mode 100644 nova/api/openstack/contrib/drive_types.py (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/drive_types.py b/nova/api/openstack/contrib/drive_types.py deleted file mode 100644 index 1aa65374f..000000000 --- a/nova/api/openstack/contrib/drive_types.py +++ /dev/null @@ -1,143 +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 Drive Types extension for Virtual Storage Arrays""" - -from webob import exc - -from nova.vsa import drive_types -from nova import exception -from nova import log as logging -from nova.api.openstack import common -from nova.api.openstack import extensions -from nova.api.openstack import faults - -LOG = logging.getLogger("nova.api.drive_types") - - -def _drive_type_view(drive): - """Maps keys for drive types view.""" - d = {} - - d['id'] = drive['id'] - d['displayName'] = drive['name'] - d['type'] = drive['type'] - d['size'] = drive['size_gb'] - d['rpm'] = drive['rpm'] - d['capabilities'] = drive['capabilities'] - return d - - -class DriveTypeController(object): - """The Drive Type API controller for the OpenStack API.""" - - _serialization_metadata = { - 'application/xml': { - "attributes": { - "drive_type": [ - "id", - "displayName", - "type", - "size", - "rpm", - "capabilities", - ]}}} - - def index(self, req): - """Returns a list of drive types.""" - - context = req.environ['nova.context'] - dtypes = drive_types.get_all(context) - limited_list = common.limited(dtypes, req) - res = [_drive_type_view(drive) for drive in limited_list] - return {'drive_types': res} - - def show(self, req, id): - """Return data about the given drive type.""" - context = req.environ['nova.context'] - - try: - drive = drive_types.get(context, id) - except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) - - return {'drive_type': _drive_type_view(drive)} - - def create(self, req, body): - """Creates a new drive type.""" - context = req.environ['nova.context'] - - if not body: - return faults.Fault(exc.HTTPUnprocessableEntity()) - - drive = body['drive_type'] - - name = drive.get('displayName') - type = drive.get('type') - size = drive.get('size') - rpm = drive.get('rpm') - capabilities = drive.get('capabilities') - - LOG.audit(_("Create drive type %(name)s for "\ - "%(type)s:%(size)s:%(rpm)s"), locals(), context=context) - - new_drive = drive_types.create(context, - type=type, - size_gb=size, - rpm=rpm, - capabilities=capabilities, - name=name) - - return {'drive_type': _drive_type_view(new_drive)} - - def delete(self, req, id): - """Deletes a drive type.""" - context = req.environ['nova.context'] - - LOG.audit(_("Delete drive type with id: %s"), id, context=context) - - try: - drive_types.delete(context, id) - except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) - - -class Drive_types(extensions.ExtensionDescriptor): - - def get_name(self): - return "DriveTypes" - - def get_alias(self): - return "zadr-drive_types" - - def get_description(self): - return "Drive Types support" - - def get_namespace(self): - return "http://docs.openstack.org/ext/drive_types/api/v1.1" - - def get_updated(self): - return "2011-06-29T00:00:00+00:00" - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension( - 'zadr-drive_types', - DriveTypeController()) - - resources.append(res) - return resources diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index 81dbc9e1f..f3e4fc849 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -106,6 +106,10 @@ class VsaController(object): 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'] @@ -114,8 +118,7 @@ class VsaController(object): vsa_list = [] for vsa in limited_list: - instances = self.compute_api.get_all(context, - search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) + instances = self._get_instances_by_vsa_id(context, vsa.get('id')) vsa_list.append(_vsa_view(context, vsa, details, instances)) return {'vsaSet': vsa_list} @@ -136,9 +139,7 @@ class VsaController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - instances = self.compute_api.get_all(context, - search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) - + instances = self._get_instances_by_vsa_id(context, vsa.get('id')) return {'vsa': _vsa_view(context, vsa, True, instances)} def create(self, req, body): @@ -171,9 +172,7 @@ class VsaController(object): vsa = self.vsa_api.create(context, **args) - instances = self.compute_api.get_all(context, - search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))}) - + instances = self._get_instances_by_vsa_id(context, vsa.get('id')) return {'vsa': _vsa_view(context, vsa, True, instances)} def delete(self, req, id): @@ -202,14 +201,14 @@ class VsaController(object): locals(), context=context) try: - instances = self.compute_api.get_all(context, - search_opts={'metadata': dict(vsa_id=str(id))}) - - if instances is None or len(instances)==0: + instances = self._get_instances_by_vsa_id(context, id) + if instances is None or len(instances) == 0: return faults.Fault(exc.HTTPNotFound()) for instance in instances: - self.network_api.allocate_for_instance(context, instance, vpn=False) + self.network_api.allocate_for_instance(context, instance, + vpn=False) + # Placeholder return except exception.NotFound: @@ -228,6 +227,7 @@ class VsaController(object): LOG.audit(_("Disassociate address from VSA %(id)s"), locals(), context=context) + # Placeholder class VsaVolumeDriveController(volumes.VolumeController): @@ -255,6 +255,7 @@ class VsaVolumeDriveController(volumes.VolumeController): def __init__(self): self.volume_api = volume.API() + self.vsa_api = vsa.API() super(VsaVolumeDriveController, self).__init__() def _translation(self, context, vol, vsa_id, details): @@ -264,7 +265,7 @@ class VsaVolumeDriveController(volumes.VolumeController): translation = volumes.translate_volume_summary_view d = translation(context, vol) - d['vsaId'] = vol[self.direction] + d['vsaId'] = vsa_id d['name'] = vol['name'] return d @@ -276,8 +277,9 @@ class VsaVolumeDriveController(volumes.VolumeController): LOG.error(_("%(obj)s with ID %(id)s not found"), locals()) raise - own_vsa_id = volume_ref[self.direction] - if own_vsa_id != int(vsa_id): + 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() @@ -286,8 +288,8 @@ class VsaVolumeDriveController(volumes.VolumeController): """Return summary or detailed list of volumes for particular VSA.""" context = req.environ['nova.context'] - vols = self.volume_api.get_all_by_vsa(context, vsa_id, - self.direction.split('_')[0]) + 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) \ @@ -317,11 +319,19 @@ class VsaVolumeDriveController(volumes.VolumeController): 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: + return faults.Fault(exc.HTTPNotFound()) - new_volume = self.volume_api.create(context, size, None, - vol.get('displayName'), - vol.get('displayDescription'), - from_vsa_id=vsa_id) + new_volume = self.volume_api.create(context, + size, + None, + vol.get('displayName'), + vol.get('displayDescription'), + volume_type=volume_type, + metadata=dict(from_vsa_id=str(vsa_id))) return {self.object: self._translation(context, new_volume, vsa_id, True)} -- cgit From 59e9adb8e2ef39474a04ead76975a1fc3f913550 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 25 Aug 2011 19:09:50 -0700 Subject: cosmetic cleanup --- nova/api/openstack/contrib/virtual_storage_arrays.py | 6 +++--- nova/api/openstack/contrib/volumes.py | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py index f3e4fc849..e09736a28 100644 --- a/nova/api/openstack/contrib/virtual_storage_arrays.py +++ b/nova/api/openstack/contrib/virtual_storage_arrays.py @@ -260,9 +260,9 @@ class VsaVolumeDriveController(volumes.VolumeController): def _translation(self, context, vol, vsa_id, details): if details: - translation = volumes.translate_volume_detail_view + translation = volumes._translate_volume_detail_view else: - translation = volumes.translate_volume_summary_view + translation = volumes._translate_volume_summary_view d = translation(context, vol) d['vsaId'] = vsa_id @@ -559,7 +559,7 @@ class Virtual_storage_arrays(extensions.ExtensionDescriptor): return "http://docs.openstack.org/ext/vsa/api/v1.1" def get_updated(self): - return "2011-06-29T00:00:00+00:00" + return "2011-08-25T00:00:00+00:00" def get_resources(self): resources = [] diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py index 8c3898867..d62225e58 100644 --- a/nova/api/openstack/contrib/volumes.py +++ b/nova/api/openstack/contrib/volumes.py @@ -37,17 +37,17 @@ LOG = logging.getLogger("nova.api.volumes") FLAGS = flags.FLAGS -def translate_volume_detail_view(context, vol): +def _translate_volume_detail_view(context, vol): """Maps keys for volumes details view.""" - d = translate_volume_summary_view(context, vol) + d = _translate_volume_summary_view(context, vol) # No additional data / lookups at the moment return d -def translate_volume_summary_view(context, vol): +def _translate_volume_summary_view(context, vol): """Maps keys for volumes summary view.""" d = {} @@ -114,7 +114,7 @@ class VolumeController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'volume': translate_volume_detail_view(context, vol)} + return {'volume': _translate_volume_detail_view(context, vol)} def delete(self, req, id): """Delete a volume.""" @@ -130,11 +130,11 @@ class VolumeController(object): def index(self, req): """Returns a summary list of volumes.""" - return self._items(req, entity_maker=translate_volume_summary_view) + return self._items(req, entity_maker=_translate_volume_summary_view) def detail(self, req): """Returns a detailed list of volumes.""" - return self._items(req, entity_maker=translate_volume_detail_view) + return self._items(req, entity_maker=_translate_volume_detail_view) def _items(self, req, entity_maker): """Returns a list of volumes, transformed through entity_maker.""" @@ -175,7 +175,7 @@ class VolumeController(object): # Work around problem that instance is lazy-loaded... new_volume = self.volume_api.get(context, new_volume['id']) - retval = translate_volume_detail_view(context, new_volume) + retval = _translate_volume_detail_view(context, new_volume) return {'volume': retval} -- cgit