VSA code redesign. Drive types completely replaced by Volume types
This commit is contained in:
214
bin/nova-manage
214
bin/nova-manage
@@ -53,6 +53,7 @@
|
|||||||
CLI interface for nova management.
|
CLI interface for nova management.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ast
|
||||||
import gettext
|
import gettext
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
@@ -64,8 +65,6 @@ import time
|
|||||||
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
import ast
|
|
||||||
|
|
||||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||||
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||||
@@ -87,15 +86,13 @@ from nova import quota
|
|||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import version
|
from nova import version
|
||||||
|
from nova import vsa
|
||||||
from nova.api.ec2 import ec2utils
|
from nova.api.ec2 import ec2utils
|
||||||
from nova.auth import manager
|
from nova.auth import manager
|
||||||
from nova.cloudpipe import pipelib
|
from nova.cloudpipe import pipelib
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
from nova.db import migration
|
from nova.db import migration
|
||||||
from nova import compute
|
from nova.volume import volume_types
|
||||||
from nova import volume
|
|
||||||
from nova import vsa
|
|
||||||
from nova.vsa import drive_types
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DECLARE('fixed_range', 'nova.network.manager')
|
flags.DECLARE('fixed_range', 'nova.network.manager')
|
||||||
@@ -1076,14 +1073,12 @@ class VsaCommands(object):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.manager = manager.AuthManager()
|
self.manager = manager.AuthManager()
|
||||||
self.vsa_api = vsa.API()
|
self.vsa_api = vsa.API()
|
||||||
self.compute_api = compute.API()
|
|
||||||
self.volume_api = volume.API()
|
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
self._format_str_vsa = "%-5s %-15s %-25s %-10s %-6s "\
|
self._format_str_vsa = "%-5s %-15s %-25s %-10s %-6s "\
|
||||||
"%-9s %-10s %-10s %10s"
|
"%-9s %-10s %-10s %10s"
|
||||||
self._format_str_volume = "\t%-4s %-15s %-5s %-10s %-20s %s"
|
self._format_str_volume = "\t%-4s %-15s %-5s %-10s %-20s %s"
|
||||||
self._format_str_drive = "\t%-4s %-15s %-5s %-10s %-20s %s"
|
self._format_str_drive = "\t%-4s %-15s %-5s %-10s %-20s %-4s %-10s %s"
|
||||||
self._format_str_instance = "\t%-4s %-10s %-20s %-12s %-10s "\
|
self._format_str_instance = "\t%-4s %-10s %-20s %-12s %-10s "\
|
||||||
"%-15s %-15s %-10s %-15s %s"
|
"%-15s %-15s %-10s %-15s %s"
|
||||||
|
|
||||||
@@ -1124,7 +1119,7 @@ class VsaCommands(object):
|
|||||||
def _print_volume(self, vol):
|
def _print_volume(self, vol):
|
||||||
print self._format_str_volume %\
|
print self._format_str_volume %\
|
||||||
(vol['id'],
|
(vol['id'],
|
||||||
vol['display_name'],
|
vol['display_name'] or vol['name'],
|
||||||
vol['size'],
|
vol['size'],
|
||||||
vol['status'],
|
vol['status'],
|
||||||
vol['attach_status'],
|
vol['attach_status'],
|
||||||
@@ -1138,15 +1133,24 @@ class VsaCommands(object):
|
|||||||
_('size'),
|
_('size'),
|
||||||
_('status'),
|
_('status'),
|
||||||
_('host'),
|
_('host'),
|
||||||
|
_('type'),
|
||||||
|
_('typeName'),
|
||||||
_('createTime'))
|
_('createTime'))
|
||||||
|
|
||||||
def _print_drive(self, drive):
|
def _print_drive(self, drive):
|
||||||
print self._format_str_volume %\
|
if drive['volume_type_id'] is not None and drive.get('volume_type'):
|
||||||
|
drive_type_name = drive['volume_type'].get('name')
|
||||||
|
else:
|
||||||
|
drive_type_name = ''
|
||||||
|
|
||||||
|
print self._format_str_drive %\
|
||||||
(drive['id'],
|
(drive['id'],
|
||||||
drive['display_name'],
|
drive['display_name'],
|
||||||
drive['size'],
|
drive['size'],
|
||||||
drive['status'],
|
drive['status'],
|
||||||
drive['host'],
|
drive['host'],
|
||||||
|
drive['volume_type_id'],
|
||||||
|
drive_type_name,
|
||||||
str(drive['created_at']))
|
str(drive['created_at']))
|
||||||
|
|
||||||
def _print_instance_header(self):
|
def _print_instance_header(self):
|
||||||
@@ -1196,9 +1200,7 @@ class VsaCommands(object):
|
|||||||
vsa_id = vsa.get('id')
|
vsa_id = vsa.get('id')
|
||||||
|
|
||||||
if print_instances:
|
if print_instances:
|
||||||
instances = self.compute_api.get_all(context,
|
instances = self.vsa_api.get_all_vsa_instances(context, vsa_id)
|
||||||
search_opts={'metadata':
|
|
||||||
dict(vsa_id=str(vsa_id))})
|
|
||||||
if instances:
|
if instances:
|
||||||
print
|
print
|
||||||
self._print_instance_header()
|
self._print_instance_header()
|
||||||
@@ -1207,8 +1209,7 @@ class VsaCommands(object):
|
|||||||
print
|
print
|
||||||
|
|
||||||
if print_drives:
|
if print_drives:
|
||||||
drives = self.volume_api.get_all_by_vsa(context,
|
drives = self.vsa_api.get_all_vsa_drives(context, vsa_id)
|
||||||
vsa_id, "to")
|
|
||||||
if drives:
|
if drives:
|
||||||
self._print_drive_header()
|
self._print_drive_header()
|
||||||
for drive in drives:
|
for drive in drives:
|
||||||
@@ -1216,8 +1217,7 @@ class VsaCommands(object):
|
|||||||
print
|
print
|
||||||
|
|
||||||
if print_volumes:
|
if print_volumes:
|
||||||
volumes = self.volume_api.get_all_by_vsa(context,
|
volumes = self.vsa_api.get_all_vsa_volumes(context, vsa_id)
|
||||||
vsa_id, "from")
|
|
||||||
if volumes:
|
if volumes:
|
||||||
self._print_volume_header()
|
self._print_volume_header()
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
@@ -1344,7 +1344,7 @@ class VsaCommands(object):
|
|||||||
|
|
||||||
@args('--id', dest='vsa_id', metavar="<vsa_id>",
|
@args('--id', dest='vsa_id', metavar="<vsa_id>",
|
||||||
help='VSA ID (optional)')
|
help='VSA ID (optional)')
|
||||||
@args('--all', dest='all', action="store_true",
|
@args('--all', dest='all', action="store_true", default=False,
|
||||||
help='Show all available details')
|
help='Show all available details')
|
||||||
@args('--drives', dest='drives', action="store_true",
|
@args('--drives', dest='drives', action="store_true",
|
||||||
help='Include drive-level details')
|
help='Include drive-level details')
|
||||||
@@ -1384,6 +1384,7 @@ class VsaDriveTypeCommands(object):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(VsaDriveTypeCommands, self).__init__(*args, **kwargs)
|
super(VsaDriveTypeCommands, self).__init__(*args, **kwargs)
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
|
self._drive_type_template = '%s_%sGB_%sRPM'
|
||||||
|
|
||||||
def _list(self, drives):
|
def _list(self, drives):
|
||||||
format_str = "%-5s %-30s %-10s %-10s %-10s %-20s %-10s %s"
|
format_str = "%-5s %-30s %-10s %-10s %-10s %-20s %-10s %s"
|
||||||
@@ -1398,75 +1399,94 @@ class VsaDriveTypeCommands(object):
|
|||||||
_('visible'),
|
_('visible'),
|
||||||
_('createTime'))
|
_('createTime'))
|
||||||
|
|
||||||
for drive in drives:
|
for name, vol_type in drives.iteritems():
|
||||||
|
drive = vol_type.get('extra_specs')
|
||||||
print format_str %\
|
print format_str %\
|
||||||
(str(drive['id']),
|
(str(vol_type['id']),
|
||||||
drive['name'],
|
drive['drive_name'],
|
||||||
drive['type'],
|
drive['drive_type'],
|
||||||
str(drive['size_gb']),
|
drive['drive_size'],
|
||||||
drive['rpm'],
|
drive['drive_rpm'],
|
||||||
drive['capabilities'],
|
drive.get('capabilities', ''),
|
||||||
str(drive['visible']),
|
str(drive.get('visible', '')),
|
||||||
str(drive['created_at']))
|
str(vol_type['created_at']))
|
||||||
|
|
||||||
@args('--type', dest='type', metavar="<type>",
|
@args('--type', dest='type', metavar="<type>",
|
||||||
help='Drive type (SATA, SAS, SSD, etc.)')
|
help='Drive type (SATA, SAS, SSD, etc.)')
|
||||||
@args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
|
@args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
|
||||||
@args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
|
@args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
|
||||||
@args('--capabilities', dest='capabilities', metavar="<string>",
|
@args('--capabilities', dest='capabilities', default=None,
|
||||||
help='Different capabilities')
|
metavar="<string>", help='Different capabilities')
|
||||||
@args('--visible', dest='visible', metavar="<show|hide>",
|
@args('--hide', dest='hide', action="store_true", default=False,
|
||||||
help='Show or hide drive')
|
help='Show or hide drive')
|
||||||
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
||||||
def create(self, type, size_gb, rpm, capabilities='',
|
def create(self, type, size_gb, rpm, capabilities=None,
|
||||||
visible=None, name=None):
|
hide=False, name=None):
|
||||||
"""Create drive type."""
|
"""Create drive type."""
|
||||||
|
|
||||||
if visible in [None, "--show", "show"]:
|
hide = True if hide in [True, "True", "--hide", "hide"] else False
|
||||||
visible = True
|
|
||||||
elif visible in ["--hide", "hide"]:
|
if name is None:
|
||||||
visible = False
|
name = self._drive_type_template % (type, size_gb, rpm)
|
||||||
|
|
||||||
|
extra_specs = {'type': 'vsa_drive',
|
||||||
|
'drive_name': name,
|
||||||
|
'drive_type': type,
|
||||||
|
'drive_size': size_gb,
|
||||||
|
'drive_rpm': rpm,
|
||||||
|
'visible': True,
|
||||||
|
}
|
||||||
|
if hide:
|
||||||
|
extra_specs['visible'] = False
|
||||||
|
|
||||||
|
if capabilities is not None and capabilities != '':
|
||||||
|
extra_specs['capabilities'] = capabilities
|
||||||
|
|
||||||
|
volume_types.create(self.context, name, extra_specs)
|
||||||
|
result = volume_types.get_volume_type_by_name(self.context, name)
|
||||||
|
self._list({name: result})
|
||||||
|
|
||||||
|
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
||||||
|
@args('--purge', action="store_true", dest='purge', default=False,
|
||||||
|
help='purge record from database')
|
||||||
|
def delete(self, name, purge):
|
||||||
|
"""Marks instance types / flavors as deleted"""
|
||||||
|
try:
|
||||||
|
if purge:
|
||||||
|
volume_types.purge(self.context, name)
|
||||||
|
verb = "purged"
|
||||||
else:
|
else:
|
||||||
raise ValueError(_('Visible parameter should be set to --show '\
|
volume_types.destroy(self.context, name)
|
||||||
'or --hide'))
|
verb = "deleted"
|
||||||
|
except exception.ApiError:
|
||||||
|
print "Valid volume type name is required"
|
||||||
|
sys.exit(1)
|
||||||
|
except exception.DBError, e:
|
||||||
|
print "DB Error: %s" % e
|
||||||
|
sys.exit(2)
|
||||||
|
except:
|
||||||
|
sys.exit(3)
|
||||||
|
else:
|
||||||
|
print "%s %s" % (name, verb)
|
||||||
|
|
||||||
result = drive_types.create(self.context,
|
@args('--all', dest='all', action="store_true", default=False,
|
||||||
type, int(size_gb), rpm,
|
help='Show all drives (including invisible)')
|
||||||
capabilities, visible, name)
|
|
||||||
self._list([result])
|
|
||||||
|
|
||||||
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
|
||||||
def delete(self, name):
|
|
||||||
"""Delete drive type."""
|
|
||||||
|
|
||||||
dtype = drive_types.get_by_name(self.context, name)
|
|
||||||
drive_types.delete(self.context, dtype['id'])
|
|
||||||
|
|
||||||
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
|
||||||
@args('--new_name', dest='new_name', metavar="<name>",
|
|
||||||
help='New Drive name (optional)')
|
|
||||||
def rename(self, name, new_name=None):
|
|
||||||
"""Rename drive type."""
|
|
||||||
|
|
||||||
dtype = drive_types.rename(self.context,
|
|
||||||
name, new_name)
|
|
||||||
self._list([dtype])
|
|
||||||
|
|
||||||
@args('--all', dest='visible', action="store_false",
|
|
||||||
help='Show all drives')
|
|
||||||
@args('--name', dest='name', metavar="<name>",
|
@args('--name', dest='name', metavar="<name>",
|
||||||
help='Show only specified drive')
|
help='Show only specified drive')
|
||||||
def list(self, visible=None, name=None):
|
def list(self, all=False, name=None):
|
||||||
"""Describe all available VSA drive types (or particular one)."""
|
"""Describe all available VSA drive types (or particular one)."""
|
||||||
|
|
||||||
visible = False if visible in ["--all", False] else True
|
all = False if all in ["--all", False, "False"] else True
|
||||||
|
|
||||||
|
search_opts = {'extra_specs': {'type': 'vsa_drive'}}
|
||||||
if name is not None:
|
if name is not None:
|
||||||
drive = drive_types.get_by_name(self.context, name)
|
search_opts['extra_specs']['name'] = name
|
||||||
drives = [drive]
|
|
||||||
else:
|
|
||||||
drives = drive_types.get_all(self.context, visible)
|
|
||||||
|
|
||||||
|
if all == False:
|
||||||
|
search_opts['extra_specs']['visible'] = '1'
|
||||||
|
|
||||||
|
drives = volume_types.get_all_types(self.context,
|
||||||
|
search_opts=search_opts)
|
||||||
self._list(drives)
|
self._list(drives)
|
||||||
|
|
||||||
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
@args('--name', dest='name', metavar="<name>", help='Drive name')
|
||||||
@@ -1474,32 +1494,44 @@ class VsaDriveTypeCommands(object):
|
|||||||
help='Drive type (SATA, SAS, SSD, etc.)')
|
help='Drive type (SATA, SAS, SSD, etc.)')
|
||||||
@args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
|
@args('--size', dest='size_gb', metavar="<gb>", help='Drive size in GB')
|
||||||
@args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
|
@args('--rpm', dest='rpm', metavar="<rpm>", help='RPM')
|
||||||
@args('--capabilities', dest='capabilities', metavar="<string>",
|
@args('--capabilities', dest='capabilities', default=None,
|
||||||
help='Different capabilities')
|
metavar="<string>", help='Different capabilities')
|
||||||
@args('--visible', dest='visible', metavar="<show|hide>",
|
@args('--visible', dest='visible',
|
||||||
help='Show or hide drive')
|
metavar="<show|hide>", help='Show or hide drive')
|
||||||
def update(self, name, type=None, size_gb=None, rpm=None,
|
def update(self, name, type=None, size_gb=None, rpm=None,
|
||||||
capabilities='', visible=None):
|
capabilities=None, visible=None):
|
||||||
"""Update drive type."""
|
"""Update drive type."""
|
||||||
|
|
||||||
values = {
|
volume_type = volume_types.get_volume_type_by_name(self.context, name)
|
||||||
'type': type,
|
|
||||||
'size_gb': size_gb,
|
|
||||||
'rpm': rpm,
|
|
||||||
'capabilities': capabilities,
|
|
||||||
}
|
|
||||||
if visible:
|
|
||||||
if visible in ["--show", "show"]:
|
|
||||||
values['visible'] = True
|
|
||||||
elif visible in ["--hide", "hide"]:
|
|
||||||
values['visible'] = False
|
|
||||||
else:
|
|
||||||
raise ValueError(_("Visible parameter should be set to "\
|
|
||||||
"--show or --hide"))
|
|
||||||
|
|
||||||
dtype = drive_types.get_by_name(self.context, name)
|
extra_specs = {'type': 'vsa_drive'}
|
||||||
dtype = drive_types.update(self.context, dtype['id'], **values)
|
|
||||||
self._list([dtype])
|
if type:
|
||||||
|
extra_specs['drive_type'] = type
|
||||||
|
|
||||||
|
if size_gb:
|
||||||
|
extra_specs['drive_size'] = size_gb
|
||||||
|
|
||||||
|
if rpm:
|
||||||
|
extra_specs['drive_rpm'] = rpm
|
||||||
|
|
||||||
|
if capabilities:
|
||||||
|
extra_specs['capabilities'] = capabilities
|
||||||
|
|
||||||
|
if visible is not None:
|
||||||
|
if visible in ["show", True, "True"]:
|
||||||
|
extra_specs['visible'] = True
|
||||||
|
elif visible in ["hide", False, "False"]:
|
||||||
|
extra_specs['visible'] = False
|
||||||
|
else:
|
||||||
|
raise ValueError(_('visible parameter should be set to '\
|
||||||
|
'show or hide'))
|
||||||
|
|
||||||
|
db.api.volume_type_extra_specs_update_or_create(self.context,
|
||||||
|
volume_type['id'],
|
||||||
|
extra_specs)
|
||||||
|
result = volume_types.get_volume_type_by_name(self.context, name)
|
||||||
|
self._list({name: result})
|
||||||
|
|
||||||
|
|
||||||
class VolumeCommands(object):
|
class VolumeCommands(object):
|
||||||
|
|||||||
10
bin/nova-vsa
10
bin/nova-vsa
@@ -4,6 +4,7 @@
|
|||||||
# Copyright (c) 2011 Zadara Storage Inc.
|
# Copyright (c) 2011 Zadara Storage Inc.
|
||||||
# Copyright (c) 2011 OpenStack LLC.
|
# Copyright (c) 2011 OpenStack LLC.
|
||||||
#
|
#
|
||||||
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
@@ -17,6 +18,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Starter script for Nova VSA."""
|
"""Starter script for Nova VSA."""
|
||||||
|
|
||||||
|
import eventlet
|
||||||
|
eventlet.monkey_patch()
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -28,6 +33,7 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
|||||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||||
sys.path.insert(0, possible_topdir)
|
sys.path.insert(0, possible_topdir)
|
||||||
|
|
||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import service
|
from nova import service
|
||||||
@@ -37,5 +43,7 @@ if __name__ == '__main__':
|
|||||||
utils.default_flagfile()
|
utils.default_flagfile()
|
||||||
flags.FLAGS(sys.argv)
|
flags.FLAGS(sys.argv)
|
||||||
logging.setup()
|
logging.setup()
|
||||||
service.serve()
|
utils.monkey_patch()
|
||||||
|
server = service.Service.create(binary='nova-vsa')
|
||||||
|
service.serve(server)
|
||||||
service.wait()
|
service.wait()
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -106,6 +106,10 @@ class VsaController(object):
|
|||||||
self.network_api = network.API()
|
self.network_api = network.API()
|
||||||
super(VsaController, self).__init__()
|
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):
|
def _items(self, req, details):
|
||||||
"""Return summary or detailed list of VSAs."""
|
"""Return summary or detailed list of VSAs."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
@@ -114,8 +118,7 @@ class VsaController(object):
|
|||||||
|
|
||||||
vsa_list = []
|
vsa_list = []
|
||||||
for vsa in limited_list:
|
for vsa in limited_list:
|
||||||
instances = self.compute_api.get_all(context,
|
instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
|
||||||
search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))})
|
|
||||||
vsa_list.append(_vsa_view(context, vsa, details, instances))
|
vsa_list.append(_vsa_view(context, vsa, details, instances))
|
||||||
return {'vsaSet': vsa_list}
|
return {'vsaSet': vsa_list}
|
||||||
|
|
||||||
@@ -136,9 +139,7 @@ class VsaController(object):
|
|||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
return faults.Fault(exc.HTTPNotFound())
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
instances = self.compute_api.get_all(context,
|
instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
|
||||||
search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))})
|
|
||||||
|
|
||||||
return {'vsa': _vsa_view(context, vsa, True, instances)}
|
return {'vsa': _vsa_view(context, vsa, True, instances)}
|
||||||
|
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
@@ -171,9 +172,7 @@ class VsaController(object):
|
|||||||
|
|
||||||
vsa = self.vsa_api.create(context, **args)
|
vsa = self.vsa_api.create(context, **args)
|
||||||
|
|
||||||
instances = self.compute_api.get_all(context,
|
instances = self._get_instances_by_vsa_id(context, vsa.get('id'))
|
||||||
search_opts={'metadata': dict(vsa_id=str(vsa.get('id')))})
|
|
||||||
|
|
||||||
return {'vsa': _vsa_view(context, vsa, True, instances)}
|
return {'vsa': _vsa_view(context, vsa, True, instances)}
|
||||||
|
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
@@ -202,14 +201,14 @@ class VsaController(object):
|
|||||||
locals(), context=context)
|
locals(), context=context)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
instances = self.compute_api.get_all(context,
|
instances = self._get_instances_by_vsa_id(context, id)
|
||||||
search_opts={'metadata': dict(vsa_id=str(id))})
|
if instances is None or len(instances) == 0:
|
||||||
|
|
||||||
if instances is None or len(instances)==0:
|
|
||||||
return faults.Fault(exc.HTTPNotFound())
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
for instance in instances:
|
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
|
return
|
||||||
|
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
@@ -228,6 +227,7 @@ class VsaController(object):
|
|||||||
|
|
||||||
LOG.audit(_("Disassociate address from VSA %(id)s"),
|
LOG.audit(_("Disassociate address from VSA %(id)s"),
|
||||||
locals(), context=context)
|
locals(), context=context)
|
||||||
|
# Placeholder
|
||||||
|
|
||||||
|
|
||||||
class VsaVolumeDriveController(volumes.VolumeController):
|
class VsaVolumeDriveController(volumes.VolumeController):
|
||||||
@@ -255,6 +255,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.volume_api = volume.API()
|
self.volume_api = volume.API()
|
||||||
|
self.vsa_api = vsa.API()
|
||||||
super(VsaVolumeDriveController, self).__init__()
|
super(VsaVolumeDriveController, self).__init__()
|
||||||
|
|
||||||
def _translation(self, context, vol, vsa_id, details):
|
def _translation(self, context, vol, vsa_id, details):
|
||||||
@@ -264,7 +265,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
|
|||||||
translation = volumes.translate_volume_summary_view
|
translation = volumes.translate_volume_summary_view
|
||||||
|
|
||||||
d = translation(context, vol)
|
d = translation(context, vol)
|
||||||
d['vsaId'] = vol[self.direction]
|
d['vsaId'] = vsa_id
|
||||||
d['name'] = vol['name']
|
d['name'] = vol['name']
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@@ -276,8 +277,9 @@ class VsaVolumeDriveController(volumes.VolumeController):
|
|||||||
LOG.error(_("%(obj)s with ID %(id)s not found"), locals())
|
LOG.error(_("%(obj)s with ID %(id)s not found"), locals())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
own_vsa_id = volume_ref[self.direction]
|
own_vsa_id = self.volume_api.get_volume_metadata_value(volume_ref,
|
||||||
if own_vsa_id != int(vsa_id):
|
self.direction)
|
||||||
|
if own_vsa_id != vsa_id:
|
||||||
LOG.error(_("%(obj)s with ID %(id)s belongs to VSA %(own_vsa_id)s"\
|
LOG.error(_("%(obj)s with ID %(id)s belongs to VSA %(own_vsa_id)s"\
|
||||||
" and not to VSA %(vsa_id)s."), locals())
|
" and not to VSA %(vsa_id)s."), locals())
|
||||||
raise exception.Invalid()
|
raise exception.Invalid()
|
||||||
@@ -286,8 +288,8 @@ class VsaVolumeDriveController(volumes.VolumeController):
|
|||||||
"""Return summary or detailed list of volumes for particular VSA."""
|
"""Return summary or detailed list of volumes for particular VSA."""
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
|
|
||||||
vols = self.volume_api.get_all_by_vsa(context, vsa_id,
|
vols = self.volume_api.get_all(context,
|
||||||
self.direction.split('_')[0])
|
search_opts={'metadata': {self.direction: str(vsa_id)}})
|
||||||
limited_list = common.limited(vols, req)
|
limited_list = common.limited(vols, req)
|
||||||
|
|
||||||
res = [self._translation(context, vol, vsa_id, details) \
|
res = [self._translation(context, vol, vsa_id, details) \
|
||||||
@@ -317,11 +319,19 @@ class VsaVolumeDriveController(volumes.VolumeController):
|
|||||||
size = vol['size']
|
size = vol['size']
|
||||||
LOG.audit(_("Create volume of %(size)s GB from VSA ID %(vsa_id)s"),
|
LOG.audit(_("Create volume of %(size)s GB from VSA ID %(vsa_id)s"),
|
||||||
locals(), context=context)
|
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,
|
new_volume = self.volume_api.create(context,
|
||||||
|
size,
|
||||||
|
None,
|
||||||
vol.get('displayName'),
|
vol.get('displayName'),
|
||||||
vol.get('displayDescription'),
|
vol.get('displayDescription'),
|
||||||
from_vsa_id=vsa_id)
|
volume_type=volume_type,
|
||||||
|
metadata=dict(from_vsa_id=str(vsa_id)))
|
||||||
|
|
||||||
return {self.object: self._translation(context, new_volume,
|
return {self.object: self._translation(context, new_volume,
|
||||||
vsa_id, True)}
|
vsa_id, True)}
|
||||||
|
|||||||
@@ -918,16 +918,6 @@ def volume_get_all_by_project(context, project_id):
|
|||||||
return IMPL.volume_get_all_by_project(context, project_id)
|
return IMPL.volume_get_all_by_project(context, project_id)
|
||||||
|
|
||||||
|
|
||||||
def volume_get_all_assigned_to_vsa(context, vsa_id):
|
|
||||||
"""Get all volumes assigned to particular VSA."""
|
|
||||||
return IMPL.volume_get_all_assigned_to_vsa(context, vsa_id)
|
|
||||||
|
|
||||||
|
|
||||||
def volume_get_all_assigned_from_vsa(context, vsa_id):
|
|
||||||
"""Get all volumes created from particular VSA."""
|
|
||||||
return IMPL.volume_get_all_assigned_from_vsa(context, vsa_id)
|
|
||||||
|
|
||||||
|
|
||||||
def volume_get_by_ec2_id(context, ec2_id):
|
def volume_get_by_ec2_id(context, ec2_id):
|
||||||
"""Get a volume by ec2 id."""
|
"""Get a volume by ec2 id."""
|
||||||
return IMPL.volume_get_by_ec2_id(context, ec2_id)
|
return IMPL.volume_get_by_ec2_id(context, ec2_id)
|
||||||
@@ -1528,36 +1518,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id,
|
|||||||
####################
|
####################
|
||||||
|
|
||||||
|
|
||||||
def drive_type_create(context, values):
|
|
||||||
"""Creates drive type record."""
|
|
||||||
return IMPL.drive_type_create(context, values)
|
|
||||||
|
|
||||||
|
|
||||||
def drive_type_update(context, drive_type_id, values):
|
|
||||||
"""Updates drive type record."""
|
|
||||||
return IMPL.drive_type_update(context, drive_type_id, values)
|
|
||||||
|
|
||||||
|
|
||||||
def drive_type_destroy(context, drive_type_id):
|
|
||||||
"""Deletes drive type record."""
|
|
||||||
return IMPL.drive_type_destroy(context, drive_type_id)
|
|
||||||
|
|
||||||
|
|
||||||
def drive_type_get(context, drive_type_id):
|
|
||||||
"""Get drive type record by id."""
|
|
||||||
return IMPL.drive_type_get(context, drive_type_id)
|
|
||||||
|
|
||||||
|
|
||||||
def drive_type_get_by_name(context, name):
|
|
||||||
"""Get drive type record by name."""
|
|
||||||
return IMPL.drive_type_get_by_name(context, name)
|
|
||||||
|
|
||||||
|
|
||||||
def drive_type_get_all(context, visible):
|
|
||||||
"""Returns all (or only visible) drive types."""
|
|
||||||
return IMPL.drive_type_get_all(context, visible)
|
|
||||||
|
|
||||||
|
|
||||||
def vsa_create(context, values):
|
def vsa_create(context, values):
|
||||||
"""Creates Virtual Storage Array record."""
|
"""Creates Virtual Storage Array record."""
|
||||||
return IMPL.vsa_create(context, values)
|
return IMPL.vsa_create(context, values)
|
||||||
@@ -1586,8 +1546,3 @@ def vsa_get_all(context):
|
|||||||
def vsa_get_all_by_project(context, project_id):
|
def vsa_get_all_by_project(context, project_id):
|
||||||
"""Get all Virtual Storage Array records by project ID."""
|
"""Get all Virtual Storage Array records by project ID."""
|
||||||
return IMPL.vsa_get_all_by_project(context, project_id)
|
return IMPL.vsa_get_all_by_project(context, project_id)
|
||||||
|
|
||||||
|
|
||||||
def vsa_get_vc_ips_list(context, vsa_id):
|
|
||||||
"""Retrieves IPs of instances associated with Virtual Storage Array."""
|
|
||||||
return IMPL.vsa_get_vc_ips_list(context, vsa_id)
|
|
||||||
|
|||||||
@@ -2226,7 +2226,6 @@ def volume_get(context, volume_id, session=None):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(id=volume_id).\
|
filter_by(id=volume_id).\
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
filter_by(deleted=can_read_deleted(context)).\
|
||||||
first()
|
first()
|
||||||
@@ -2235,7 +2234,6 @@ def volume_get(context, volume_id, session=None):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(project_id=context.project_id).\
|
filter_by(project_id=context.project_id).\
|
||||||
filter_by(id=volume_id).\
|
filter_by(id=volume_id).\
|
||||||
filter_by(deleted=False).\
|
filter_by(deleted=False).\
|
||||||
@@ -2253,7 +2251,6 @@ def volume_get_all(context):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
filter_by(deleted=can_read_deleted(context)).\
|
||||||
all()
|
all()
|
||||||
|
|
||||||
@@ -2265,7 +2262,6 @@ def volume_get_all_by_host(context, host):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(host=host).\
|
filter_by(host=host).\
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
filter_by(deleted=can_read_deleted(context)).\
|
||||||
all()
|
all()
|
||||||
@@ -2277,7 +2273,6 @@ def volume_get_all_by_instance(context, instance_id):
|
|||||||
result = session.query(models.Volume).\
|
result = session.query(models.Volume).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(instance_id=instance_id).\
|
filter_by(instance_id=instance_id).\
|
||||||
filter_by(deleted=False).\
|
filter_by(deleted=False).\
|
||||||
all()
|
all()
|
||||||
@@ -2286,28 +2281,6 @@ def volume_get_all_by_instance(context, instance_id):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
|
||||||
def volume_get_all_assigned_to_vsa(context, vsa_id):
|
|
||||||
session = get_session()
|
|
||||||
result = session.query(models.Volume).\
|
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(to_vsa_id=vsa_id).\
|
|
||||||
filter_by(deleted=False).\
|
|
||||||
all()
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
|
||||||
def volume_get_all_assigned_from_vsa(context, vsa_id):
|
|
||||||
session = get_session()
|
|
||||||
result = session.query(models.Volume).\
|
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(from_vsa_id=vsa_id).\
|
|
||||||
filter_by(deleted=False).\
|
|
||||||
all()
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def volume_get_all_by_project(context, project_id):
|
def volume_get_all_by_project(context, project_id):
|
||||||
authorize_project_context(context, project_id)
|
authorize_project_context(context, project_id)
|
||||||
@@ -2317,7 +2290,6 @@ def volume_get_all_by_project(context, project_id):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
filter_by(project_id=project_id).\
|
filter_by(project_id=project_id).\
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
filter_by(deleted=can_read_deleted(context)).\
|
||||||
all()
|
all()
|
||||||
@@ -2332,7 +2304,6 @@ def volume_get_instance(context, volume_id):
|
|||||||
options(joinedload('instance')).\
|
options(joinedload('instance')).\
|
||||||
options(joinedload('volume_metadata')).\
|
options(joinedload('volume_metadata')).\
|
||||||
options(joinedload('volume_type')).\
|
options(joinedload('volume_type')).\
|
||||||
options(joinedload('drive_type')).\
|
|
||||||
first()
|
first()
|
||||||
if not result:
|
if not result:
|
||||||
raise exception.VolumeNotFound(volume_id=volume_id)
|
raise exception.VolumeNotFound(volume_id=volume_id)
|
||||||
@@ -2377,7 +2348,7 @@ def volume_update(context, volume_id, values):
|
|||||||
volume_ref = volume_get(context, volume_id, session=session)
|
volume_ref = volume_get(context, volume_id, session=session)
|
||||||
volume_ref.update(values)
|
volume_ref.update(values)
|
||||||
volume_ref.save(session=session)
|
volume_ref.save(session=session)
|
||||||
return volume_ref
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
||||||
@@ -3871,106 +3842,6 @@ def volume_type_extra_specs_update_or_create(context, volume_type_id,
|
|||||||
####################
|
####################
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
|
||||||
def drive_type_create(context, values):
|
|
||||||
"""
|
|
||||||
Creates drive type record.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
drive_type_ref = models.DriveTypes()
|
|
||||||
drive_type_ref.update(values)
|
|
||||||
drive_type_ref.save()
|
|
||||||
except Exception, e:
|
|
||||||
raise exception.DBError(e)
|
|
||||||
return drive_type_ref
|
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
|
||||||
def drive_type_update(context, drive_type_id, values):
|
|
||||||
"""
|
|
||||||
Updates drive type record.
|
|
||||||
"""
|
|
||||||
session = get_session()
|
|
||||||
with session.begin():
|
|
||||||
drive_type_ref = drive_type_get(context, drive_type_id,
|
|
||||||
session=session)
|
|
||||||
drive_type_ref.update(values)
|
|
||||||
drive_type_ref.save(session=session)
|
|
||||||
return drive_type_ref
|
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
|
||||||
def drive_type_destroy(context, drive_type_id):
|
|
||||||
"""
|
|
||||||
Deletes drive type record.
|
|
||||||
"""
|
|
||||||
session = get_session()
|
|
||||||
drive_type_ref = session.query(models.DriveTypes).\
|
|
||||||
filter_by(id=drive_type_id)
|
|
||||||
records = drive_type_ref.delete()
|
|
||||||
if records == 0:
|
|
||||||
raise exception.VirtualDiskTypeNotFound(id=drive_type_id)
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def drive_type_get(context, drive_type_id, session=None):
|
|
||||||
"""
|
|
||||||
Get drive type record by id.
|
|
||||||
"""
|
|
||||||
if not session:
|
|
||||||
session = get_session()
|
|
||||||
|
|
||||||
result = session.query(models.DriveTypes).\
|
|
||||||
filter_by(id=drive_type_id).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
first()
|
|
||||||
if not result:
|
|
||||||
raise exception.VirtualDiskTypeNotFound(id=drive_type_id)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def drive_type_get_by_name(context, name, session=None):
|
|
||||||
"""
|
|
||||||
Get drive type record by name.
|
|
||||||
"""
|
|
||||||
if not session:
|
|
||||||
session = get_session()
|
|
||||||
|
|
||||||
result = session.query(models.DriveTypes).\
|
|
||||||
filter_by(name=name).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
first()
|
|
||||||
if not result:
|
|
||||||
raise exception.VirtualDiskTypeNotFoundByName(name=name)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def drive_type_get_all(context, visible):
|
|
||||||
"""
|
|
||||||
Returns all (or only visible) drive types.
|
|
||||||
"""
|
|
||||||
session = get_session()
|
|
||||||
if visible:
|
|
||||||
drive_types = session.query(models.DriveTypes).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
filter_by(visible=True).\
|
|
||||||
order_by("name").\
|
|
||||||
all()
|
|
||||||
else:
|
|
||||||
drive_types = session.query(models.DriveTypes).\
|
|
||||||
filter_by(deleted=can_read_deleted(context)).\
|
|
||||||
order_by("name").\
|
|
||||||
all()
|
|
||||||
return drive_types
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
@require_admin_context
|
||||||
def vsa_create(context, values):
|
def vsa_create(context, values):
|
||||||
"""
|
"""
|
||||||
@@ -4067,26 +3938,4 @@ def vsa_get_all_by_project(context, project_id):
|
|||||||
all()
|
all()
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
|
||||||
def vsa_get_vc_ips_list(context, vsa_id):
|
|
||||||
"""
|
|
||||||
Retrieves IPs of instances associated with Virtual Storage Array.
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
|
|
||||||
vc_instances = instance_get_all_by_filters(context,
|
|
||||||
search_opts={'metadata': dict(vsa_id=str(vsa_id))})
|
|
||||||
for vc_instance in vc_instances:
|
|
||||||
if vc_instance['fixed_ips']:
|
|
||||||
for fixed in vc_instance['fixed_ips']:
|
|
||||||
# insert the [floating,fixed] (if exists) in the head,
|
|
||||||
# otherwise append the [none,fixed] in the tail
|
|
||||||
ip = {}
|
|
||||||
ip['fixed'] = fixed['address']
|
|
||||||
if fixed['floating_ips']:
|
|
||||||
ip['floating'] = fixed['floating_ips'][0]['address']
|
|
||||||
result.append(ip)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|||||||
@@ -22,19 +22,7 @@ from nova import log as logging
|
|||||||
|
|
||||||
meta = MetaData()
|
meta = MetaData()
|
||||||
|
|
||||||
# Just for the ForeignKey and column creation to succeed, these are not the
|
|
||||||
# actual definitions of tables .
|
|
||||||
#
|
#
|
||||||
|
|
||||||
volumes = Table('volumes', meta,
|
|
||||||
Column('id', Integer(), primary_key=True, nullable=False),
|
|
||||||
)
|
|
||||||
|
|
||||||
to_vsa_id = Column('to_vsa_id', Integer(), nullable=True)
|
|
||||||
from_vsa_id = Column('from_vsa_id', Integer(), nullable=True)
|
|
||||||
drive_type_id = Column('drive_type_id', Integer(), nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
# New Tables
|
# New Tables
|
||||||
#
|
#
|
||||||
|
|
||||||
@@ -67,67 +55,21 @@ virtual_storage_arrays = Table('virtual_storage_arrays', meta,
|
|||||||
unicode_error=None, _warn_on_bytestring=False)),
|
unicode_error=None, _warn_on_bytestring=False)),
|
||||||
)
|
)
|
||||||
|
|
||||||
drive_types = Table('drive_types', meta,
|
|
||||||
Column('created_at', DateTime(timezone=False)),
|
|
||||||
Column('updated_at', DateTime(timezone=False)),
|
|
||||||
Column('deleted_at', DateTime(timezone=False)),
|
|
||||||
Column('deleted', Boolean(create_constraint=True, name=None)),
|
|
||||||
Column('id', Integer(), primary_key=True, nullable=False),
|
|
||||||
Column('name',
|
|
||||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
|
||||||
unicode_error=None, _warn_on_bytestring=False),
|
|
||||||
unique=True),
|
|
||||||
Column('type',
|
|
||||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
|
||||||
unicode_error=None, _warn_on_bytestring=False)),
|
|
||||||
Column('size_gb', Integer(), nullable=False),
|
|
||||||
Column('rpm',
|
|
||||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
|
||||||
unicode_error=None, _warn_on_bytestring=False)),
|
|
||||||
Column('capabilities',
|
|
||||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
|
||||||
unicode_error=None, _warn_on_bytestring=False)),
|
|
||||||
Column('visible', Boolean(create_constraint=True, name=None)),
|
|
||||||
)
|
|
||||||
|
|
||||||
new_tables = (virtual_storage_arrays, drive_types)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Tables to alter
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade(migrate_engine):
|
def upgrade(migrate_engine):
|
||||||
|
|
||||||
from nova import context
|
|
||||||
from nova import db
|
|
||||||
from nova import flags
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
# Upgrade operations go here. Don't create your own engine;
|
# Upgrade operations go here. Don't create your own engine;
|
||||||
# bind migrate_engine to your metadata
|
# bind migrate_engine to your metadata
|
||||||
meta.bind = migrate_engine
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
for table in new_tables:
|
|
||||||
try:
|
try:
|
||||||
table.create()
|
virtual_storage_arrays.create()
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.info(repr(table))
|
logging.info(repr(table))
|
||||||
logging.exception('Exception while creating table')
|
logging.exception('Exception while creating table')
|
||||||
raise
|
raise
|
||||||
|
|
||||||
volumes.create_column(to_vsa_id)
|
|
||||||
volumes.create_column(from_vsa_id)
|
|
||||||
volumes.create_column(drive_type_id)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade(migrate_engine):
|
def downgrade(migrate_engine):
|
||||||
meta.bind = migrate_engine
|
meta.bind = migrate_engine
|
||||||
|
|
||||||
volumes.drop_column(to_vsa_id)
|
virtual_storage_arrays.drop()
|
||||||
volumes.drop_column(from_vsa_id)
|
|
||||||
volumes.drop_column(drive_type_id)
|
|
||||||
|
|
||||||
for table in new_tables:
|
|
||||||
table.drop()
|
|
||||||
@@ -352,13 +352,6 @@ class Volume(BASE, NovaBase):
|
|||||||
|
|
||||||
volume_type_id = Column(Integer)
|
volume_type_id = Column(Integer)
|
||||||
|
|
||||||
to_vsa_id = Column(Integer,
|
|
||||||
ForeignKey('virtual_storage_arrays.id'), nullable=True)
|
|
||||||
from_vsa_id = Column(Integer,
|
|
||||||
ForeignKey('virtual_storage_arrays.id'), nullable=True)
|
|
||||||
drive_type_id = Column(Integer,
|
|
||||||
ForeignKey('drive_types.id'), nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeMetadata(BASE, NovaBase):
|
class VolumeMetadata(BASE, NovaBase):
|
||||||
"""Represents a metadata key/value pair for a volume"""
|
"""Represents a metadata key/value pair for a volume"""
|
||||||
@@ -402,38 +395,6 @@ class VolumeTypeExtraSpecs(BASE, NovaBase):
|
|||||||
'VolumeTypeExtraSpecs.deleted == False)')
|
'VolumeTypeExtraSpecs.deleted == False)')
|
||||||
|
|
||||||
|
|
||||||
class DriveTypes(BASE, NovaBase):
|
|
||||||
"""Represents the known drive types (storage media)."""
|
|
||||||
__tablename__ = 'drive_types'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
||||||
|
|
||||||
"""
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
if self.capabilities:
|
|
||||||
return FLAGS.drive_type_template_long % \
|
|
||||||
(self.type, str(self.size_gb), self.rpm, self.capabilities)
|
|
||||||
else:
|
|
||||||
return FLAGS.drive_type_template_short % \
|
|
||||||
(self.type, str(self.size_gb), self.rpm)
|
|
||||||
"""
|
|
||||||
|
|
||||||
name = Column(String(255), unique=True)
|
|
||||||
type = Column(String(255))
|
|
||||||
size_gb = Column(Integer)
|
|
||||||
rpm = Column(String(255))
|
|
||||||
capabilities = Column(String(255))
|
|
||||||
|
|
||||||
visible = Column(Boolean, default=True)
|
|
||||||
|
|
||||||
volumes = relationship(Volume,
|
|
||||||
backref=backref('drive_type', uselist=False),
|
|
||||||
foreign_keys=id,
|
|
||||||
primaryjoin='and_(Volume.drive_type_id == '
|
|
||||||
'DriveTypes.id)')
|
|
||||||
|
|
||||||
|
|
||||||
class Quota(BASE, NovaBase):
|
class Quota(BASE, NovaBase):
|
||||||
"""Represents a single quota override for a project.
|
"""Represents a single quota override for a project.
|
||||||
|
|
||||||
@@ -918,7 +879,9 @@ def register_models():
|
|||||||
Network, SecurityGroup, SecurityGroupIngressRule,
|
Network, SecurityGroup, SecurityGroupIngressRule,
|
||||||
SecurityGroupInstanceAssociation, AuthToken, User,
|
SecurityGroupInstanceAssociation, AuthToken, User,
|
||||||
Project, Certificate, ConsolePool, Console, Zone,
|
Project, Certificate, ConsolePool, Console, Zone,
|
||||||
AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration)
|
VolumeMetadata, VolumeTypes, VolumeTypeExtraSpecs,
|
||||||
|
AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration,
|
||||||
|
VirtualStorageArray)
|
||||||
engine = create_engine(FLAGS.sql_connection, echo=False)
|
engine = create_engine(FLAGS.sql_connection, echo=False)
|
||||||
for model in models:
|
for model in models:
|
||||||
model.metadata.create_all(engine)
|
model.metadata.create_all(engine)
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ import nova.exception
|
|||||||
import nova.flags
|
import nova.flags
|
||||||
import nova.log
|
import nova.log
|
||||||
|
|
||||||
|
|
||||||
FLAGS = nova.flags.FLAGS
|
FLAGS = nova.flags.FLAGS
|
||||||
LOG = nova.log.getLogger("nova.db.sqlalchemy")
|
LOG = nova.log.getLogger("nova.db.sqlalchemy")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
@@ -365,10 +365,6 @@ class VolumeTypeExtraSpecsNotFound(NotFound):
|
|||||||
"key %(extra_specs_key)s.")
|
"key %(extra_specs_key)s.")
|
||||||
|
|
||||||
|
|
||||||
class VolumeNotFoundForVsa(VolumeNotFound):
|
|
||||||
message = _("Volume not found for vsa %(vsa_id)s.")
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotNotFound(NotFound):
|
class SnapshotNotFound(NotFound):
|
||||||
message = _("Snapshot %(snapshot_id)s could not be found.")
|
message = _("Snapshot %(snapshot_id)s could not be found.")
|
||||||
|
|
||||||
@@ -799,14 +795,6 @@ class VirtualStorageArrayNotFoundByName(NotFound):
|
|||||||
message = _("Virtual Storage Array %(name)s could not be found.")
|
message = _("Virtual Storage Array %(name)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
class VirtualDiskTypeNotFound(NotFound):
|
|
||||||
message = _("Drive Type %(id)d could not be found.")
|
|
||||||
|
|
||||||
|
|
||||||
class VirtualDiskTypeNotFoundByName(NotFound):
|
|
||||||
message = _("Drive Type %(name)s could not be found.")
|
|
||||||
|
|
||||||
|
|
||||||
class CannotResizeToSameSize(NovaException):
|
class CannotResizeToSameSize(NovaException):
|
||||||
message = _("When resizing, instances must change size!")
|
message = _("When resizing, instances must change size!")
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import os
|
import os
|
||||||
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@@ -258,7 +259,6 @@ class NovaRootLogger(NovaLogger):
|
|||||||
self.addHandler(self.filelog)
|
self.addHandler(self.filelog)
|
||||||
self.logpath = logpath
|
self.logpath = logpath
|
||||||
|
|
||||||
import stat
|
|
||||||
st = os.stat(self.logpath)
|
st = os.stat(self.logpath)
|
||||||
if st.st_mode != (stat.S_IFREG | FLAGS.logfile_mode):
|
if st.st_mode != (stat.S_IFREG | FLAGS.logfile_mode):
|
||||||
os.chmod(self.logpath, FLAGS.logfile_mode)
|
os.chmod(self.logpath, FLAGS.logfile_mode)
|
||||||
|
|||||||
@@ -508,7 +508,6 @@ def get_dhcp_hosts(context, network_ref):
|
|||||||
if network_ref['multi_host'] and FLAGS.host != host:
|
if network_ref['multi_host'] and FLAGS.host != host:
|
||||||
continue
|
continue
|
||||||
hosts.append(_host_dhcp(fixed_ref))
|
hosts.append(_host_dhcp(fixed_ref))
|
||||||
|
|
||||||
return '\n'.join(hosts)
|
return '\n'.join(hosts)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ def allowed_volumes(context, requested_volumes, size):
|
|||||||
allowed_gigabytes = _get_request_allotment(requested_gigabytes,
|
allowed_gigabytes = _get_request_allotment(requested_gigabytes,
|
||||||
used_gigabytes,
|
used_gigabytes,
|
||||||
quota['gigabytes'])
|
quota['gigabytes'])
|
||||||
|
if size != 0:
|
||||||
allowed_volumes = min(allowed_volumes,
|
allowed_volumes = min(allowed_volumes,
|
||||||
int(allowed_gigabytes // size))
|
int(allowed_gigabytes // size))
|
||||||
return min(requested_volumes, allowed_volumes)
|
return min(requested_volumes, allowed_volumes)
|
||||||
|
|||||||
@@ -20,15 +20,15 @@ VSA Simple Scheduler
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import rpc
|
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import flags
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.vsa.api import VsaState
|
|
||||||
from nova.volume import api as volume_api
|
|
||||||
from nova.scheduler import driver
|
from nova.scheduler import driver
|
||||||
from nova.scheduler import simple
|
from nova.scheduler import simple
|
||||||
from nova import log as logging
|
from nova.vsa.api import VsaState
|
||||||
|
from nova.volume import volume_types
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.scheduler.vsa')
|
LOG = logging.getLogger('nova.scheduler.vsa')
|
||||||
|
|
||||||
@@ -67,21 +67,21 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
def _compare_names(str1, str2):
|
def _compare_names(str1, str2):
|
||||||
return str1.lower() == str2.lower()
|
return str1.lower() == str2.lower()
|
||||||
|
|
||||||
def _compare_sizes_approxim(cap_capacity, size_gb):
|
def _compare_sizes_approxim(cap_capacity, size):
|
||||||
cap_capacity = BYTES_TO_GB(int(cap_capacity))
|
cap_capacity = BYTES_TO_GB(int(cap_capacity))
|
||||||
size_gb = int(size_gb)
|
size = int(size)
|
||||||
size_perc = size_gb * \
|
size_perc = size * \
|
||||||
FLAGS.drive_type_approx_capacity_percent / 100
|
FLAGS.drive_type_approx_capacity_percent / 100
|
||||||
|
|
||||||
return cap_capacity >= size_gb - size_perc and \
|
return cap_capacity >= size - size_perc and \
|
||||||
cap_capacity <= size_gb + size_perc
|
cap_capacity <= size + size_perc
|
||||||
|
|
||||||
# Add more entries for additional comparisons
|
# Add more entries for additional comparisons
|
||||||
compare_list = [{'cap1': 'DriveType',
|
compare_list = [{'cap1': 'DriveType',
|
||||||
'cap2': 'type',
|
'cap2': 'type',
|
||||||
'cmp_func': _compare_names},
|
'cmp_func': _compare_names},
|
||||||
{'cap1': 'DriveCapacity',
|
{'cap1': 'DriveCapacity',
|
||||||
'cap2': 'size_gb',
|
'cap2': 'size',
|
||||||
'cmp_func': _compare_sizes_approxim}]
|
'cmp_func': _compare_sizes_approxim}]
|
||||||
|
|
||||||
for cap in compare_list:
|
for cap in compare_list:
|
||||||
@@ -193,8 +193,8 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
'attach_status': "detached",
|
'attach_status': "detached",
|
||||||
'display_name': vol['name'],
|
'display_name': vol['name'],
|
||||||
'display_description': vol['description'],
|
'display_description': vol['description'],
|
||||||
'to_vsa_id': vsa_id,
|
'volume_type_id': vol['volume_type_id'],
|
||||||
'drive_type_id': vol['drive_ref']['id'],
|
'metadata': dict(to_vsa_id=vsa_id),
|
||||||
'host': vol['host'],
|
'host': vol['host'],
|
||||||
'scheduled_at': now
|
'scheduled_at': now
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,8 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
|
|
||||||
def _assign_hosts_to_volumes(self, context, volume_params, forced_host):
|
def _assign_hosts_to_volumes(self, context, volume_params, forced_host):
|
||||||
|
|
||||||
prev_drive_type_id = None
|
prev_volume_type_id = None
|
||||||
|
request_spec = {}
|
||||||
selected_hosts = []
|
selected_hosts = []
|
||||||
|
|
||||||
LOG.debug(_("volume_params %(volume_params)s") % locals())
|
LOG.debug(_("volume_params %(volume_params)s") % locals())
|
||||||
@@ -244,14 +245,25 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
vol['capabilities'] = None
|
vol['capabilities'] = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
drive_type = vol['drive_ref']
|
volume_type_id = vol['volume_type_id']
|
||||||
request_spec = {'size': vol['size'],
|
request_spec['size'] = vol['size']
|
||||||
'drive_type': dict(drive_type)}
|
|
||||||
|
|
||||||
if prev_drive_type_id != drive_type['id']:
|
if prev_volume_type_id is None or\
|
||||||
|
prev_volume_type_id != volume_type_id:
|
||||||
# generate list of hosts for this drive type
|
# 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)
|
all_hosts = self._filter_hosts("volume", request_spec)
|
||||||
prev_drive_type_id = drive_type['id']
|
prev_volume_type_id = volume_type_id
|
||||||
|
|
||||||
(host, qos_cap) = self._select_hosts(request_spec,
|
(host, qos_cap) = self._select_hosts(request_spec,
|
||||||
all_hosts, selected_hosts)
|
all_hosts, selected_hosts)
|
||||||
@@ -279,8 +291,7 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
self._provision_volume(context, vol, vsa_id, availability_zone)
|
self._provision_volume(context, vol, vsa_id, availability_zone)
|
||||||
except:
|
except:
|
||||||
if vsa_id:
|
if vsa_id:
|
||||||
db.vsa_update(context, vsa_id,
|
db.vsa_update(context, vsa_id, dict(status=VsaState.FAILED))
|
||||||
dict(status=VsaState.FAILED))
|
|
||||||
|
|
||||||
for vol in volume_params:
|
for vol in volume_params:
|
||||||
if 'capabilities' in vol:
|
if 'capabilities' in vol:
|
||||||
@@ -302,12 +313,23 @@ class VsaScheduler(simple.SimpleScheduler):
|
|||||||
'scheduled_at': now})
|
'scheduled_at': now})
|
||||||
return host
|
return host
|
||||||
|
|
||||||
drive_type = volume_ref['drive_type']
|
volume_type_id = volume_ref['volume_type_id']
|
||||||
if drive_type is None:
|
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'])
|
LOG.debug(_("Non-VSA volume %d"), volume_ref['id'])
|
||||||
return super(VsaScheduler, self).schedule_create_volume(context,
|
return super(VsaScheduler, self).schedule_create_volume(context,
|
||||||
volume_id, *_args, **_kwargs)
|
volume_id, *_args, **_kwargs)
|
||||||
drive_type = dict(drive_type)
|
|
||||||
|
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 "\
|
LOG.debug(_("Spawning volume %(volume_id)s with drive type "\
|
||||||
"%(drive_type)s"), locals())
|
"%(drive_type)s"), locals())
|
||||||
|
|||||||
@@ -1,192 +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 json
|
|
||||||
import stubout
|
|
||||||
import webob
|
|
||||||
|
|
||||||
#from nova import compute
|
|
||||||
from nova.vsa import drive_types
|
|
||||||
from nova import exception
|
|
||||||
from nova import context
|
|
||||||
from nova import test
|
|
||||||
from nova import log as logging
|
|
||||||
from nova.tests.api.openstack import fakes
|
|
||||||
|
|
||||||
from nova.api.openstack.contrib.drive_types import _drive_type_view
|
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.tests.api.openstack.drive_types')
|
|
||||||
|
|
||||||
last_param = {}
|
|
||||||
|
|
||||||
|
|
||||||
def _get_default_drive_type():
|
|
||||||
param = {
|
|
||||||
'name': 'Test drive type',
|
|
||||||
'type': 'SATA',
|
|
||||||
'size_gb': 123,
|
|
||||||
'rpm': '7200',
|
|
||||||
'capabilities': '',
|
|
||||||
'visible': True
|
|
||||||
}
|
|
||||||
return param
|
|
||||||
|
|
||||||
|
|
||||||
def _create(context, **param):
|
|
||||||
global last_param
|
|
||||||
LOG.debug(_("_create: %s"), param)
|
|
||||||
param['id'] = 123
|
|
||||||
last_param = param
|
|
||||||
return param
|
|
||||||
|
|
||||||
|
|
||||||
def _delete(context, id):
|
|
||||||
global last_param
|
|
||||||
last_param = dict(id=id)
|
|
||||||
|
|
||||||
LOG.debug(_("_delete: %s"), locals())
|
|
||||||
|
|
||||||
|
|
||||||
def _get(context, id):
|
|
||||||
global last_param
|
|
||||||
last_param = dict(id=id)
|
|
||||||
|
|
||||||
LOG.debug(_("_get: %s"), locals())
|
|
||||||
if id != '123':
|
|
||||||
raise exception.NotFound
|
|
||||||
|
|
||||||
dtype = _get_default_drive_type()
|
|
||||||
dtype['id'] = id
|
|
||||||
return dtype
|
|
||||||
|
|
||||||
|
|
||||||
def _get_all(context, visible=True):
|
|
||||||
LOG.debug(_("_get_all: %s"), locals())
|
|
||||||
dtype = _get_default_drive_type()
|
|
||||||
dtype['id'] = 123
|
|
||||||
return [dtype]
|
|
||||||
|
|
||||||
|
|
||||||
class DriveTypesApiTest(test.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(DriveTypesApiTest, self).setUp()
|
|
||||||
self.stubs = stubout.StubOutForTesting()
|
|
||||||
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(drive_types, "create", _create)
|
|
||||||
self.stubs.Set(drive_types, "delete", _delete)
|
|
||||||
self.stubs.Set(drive_types, "get", _get)
|
|
||||||
self.stubs.Set(drive_types, "get_all", _get_all)
|
|
||||||
|
|
||||||
self.context = context.get_admin_context()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.stubs.UnsetAll()
|
|
||||||
super(DriveTypesApiTest, self).tearDown()
|
|
||||||
|
|
||||||
def test_drive_types_api_create(self):
|
|
||||||
global last_param
|
|
||||||
last_param = {}
|
|
||||||
|
|
||||||
dtype = _get_default_drive_type()
|
|
||||||
dtype['id'] = 123
|
|
||||||
|
|
||||||
body = dict(drive_type=_drive_type_view(dtype))
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-drive_types')
|
|
||||||
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
|
|
||||||
for k, v in last_param.iteritems():
|
|
||||||
self.assertEqual(last_param[k], dtype[k])
|
|
||||||
|
|
||||||
resp_dict = json.loads(resp.body)
|
|
||||||
|
|
||||||
# Compare response
|
|
||||||
self.assertTrue('drive_type' in resp_dict)
|
|
||||||
resp_dtype = resp_dict['drive_type']
|
|
||||||
self.assertEqual(resp_dtype, _drive_type_view(dtype))
|
|
||||||
|
|
||||||
def test_drive_types_api_delete(self):
|
|
||||||
global last_param
|
|
||||||
last_param = {}
|
|
||||||
|
|
||||||
dtype_id = 123
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-drive_types/%d' % dtype_id)
|
|
||||||
req.method = 'DELETE'
|
|
||||||
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(resp.status_int, 200)
|
|
||||||
self.assertEqual(str(last_param['id']), str(dtype_id))
|
|
||||||
|
|
||||||
def test_drive_types_show(self):
|
|
||||||
global last_param
|
|
||||||
last_param = {}
|
|
||||||
|
|
||||||
dtype_id = 123
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-drive_types/%d' % dtype_id)
|
|
||||||
req.method = 'GET'
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(resp.status_int, 200)
|
|
||||||
self.assertEqual(str(last_param['id']), str(dtype_id))
|
|
||||||
|
|
||||||
resp_dict = json.loads(resp.body)
|
|
||||||
|
|
||||||
# Compare response
|
|
||||||
self.assertTrue('drive_type' in resp_dict)
|
|
||||||
resp_dtype = resp_dict['drive_type']
|
|
||||||
exp_dtype = _get_default_drive_type()
|
|
||||||
exp_dtype['id'] = dtype_id
|
|
||||||
exp_dtype_view = _drive_type_view(exp_dtype)
|
|
||||||
for k, v in exp_dtype_view.iteritems():
|
|
||||||
self.assertEqual(str(resp_dtype[k]), str(exp_dtype_view[k]))
|
|
||||||
|
|
||||||
def test_drive_types_show_invalid_id(self):
|
|
||||||
global last_param
|
|
||||||
last_param = {}
|
|
||||||
|
|
||||||
dtype_id = 234
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-drive_types/%d' % dtype_id)
|
|
||||||
req.method = 'GET'
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(resp.status_int, 404)
|
|
||||||
self.assertEqual(str(last_param['id']), str(dtype_id))
|
|
||||||
|
|
||||||
def test_drive_types_index(self):
|
|
||||||
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-drive_types')
|
|
||||||
req.method = 'GET'
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
|
||||||
self.assertEqual(resp.status_int, 200)
|
|
||||||
|
|
||||||
resp_dict = json.loads(resp.body)
|
|
||||||
|
|
||||||
self.assertTrue('drive_types' in resp_dict)
|
|
||||||
resp_dtypes = resp_dict['drive_types']
|
|
||||||
self.assertEqual(len(resp_dtypes), 1)
|
|
||||||
|
|
||||||
resp_dtype = resp_dtypes.pop()
|
|
||||||
exp_dtype = _get_default_drive_type()
|
|
||||||
exp_dtype['id'] = 123
|
|
||||||
exp_dtype_view = _drive_type_view(exp_dtype)
|
|
||||||
for k, v in exp_dtype_view.iteritems():
|
|
||||||
self.assertEqual(str(resp_dtype[k]), str(exp_dtype_view[k]))
|
|
||||||
@@ -18,15 +18,14 @@ import stubout
|
|||||||
import unittest
|
import unittest
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import vsa
|
|
||||||
from nova import db
|
|
||||||
from nova import volume
|
|
||||||
from nova import context
|
|
||||||
from nova import test
|
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
|
from nova import test
|
||||||
|
from nova import volume
|
||||||
|
from nova import vsa
|
||||||
from nova.api import openstack
|
from nova.api import openstack
|
||||||
from nova.tests.api.openstack import fakes
|
from nova.tests.api.openstack import fakes
|
||||||
import nova.wsgi
|
import nova.wsgi
|
||||||
@@ -120,7 +119,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
vsa = {"displayName": "VSA Test Name",
|
vsa = {"displayName": "VSA Test Name",
|
||||||
"displayDescription": "VSA Test Desc"}
|
"displayDescription": "VSA Test Desc"}
|
||||||
body = dict(vsa=vsa)
|
body = dict(vsa=vsa)
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa')
|
req = webob.Request.blank('/v1.1/777/zadr-vsa')
|
||||||
req.method = 'POST'
|
req.method = 'POST'
|
||||||
req.body = json.dumps(body)
|
req.body = json.dumps(body)
|
||||||
req.headers['content-type'] = 'application/json'
|
req.headers['content-type'] = 'application/json'
|
||||||
@@ -139,7 +138,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
vsa['displayDescription'])
|
vsa['displayDescription'])
|
||||||
|
|
||||||
def test_vsa_create_no_body(self):
|
def test_vsa_create_no_body(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa')
|
req = webob.Request.blank('/v1.1/777/zadr-vsa')
|
||||||
req.method = 'POST'
|
req.method = 'POST'
|
||||||
req.body = json.dumps({})
|
req.body = json.dumps({})
|
||||||
req.headers['content-type'] = 'application/json'
|
req.headers['content-type'] = 'application/json'
|
||||||
@@ -152,7 +151,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
last_param = {}
|
last_param = {}
|
||||||
|
|
||||||
vsa_id = 123
|
vsa_id = 123
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/%d' % vsa_id)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
|
||||||
req.method = 'DELETE'
|
req.method = 'DELETE'
|
||||||
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
@@ -164,7 +163,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
last_param = {}
|
last_param = {}
|
||||||
|
|
||||||
vsa_id = 234
|
vsa_id = 234
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/%d' % vsa_id)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
|
||||||
req.method = 'DELETE'
|
req.method = 'DELETE'
|
||||||
|
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
@@ -176,7 +175,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
last_param = {}
|
last_param = {}
|
||||||
|
|
||||||
vsa_id = 123
|
vsa_id = 123
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/%d' % vsa_id)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
|
||||||
req.method = 'GET'
|
req.method = 'GET'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
@@ -191,14 +190,14 @@ class VSAApiTest(test.TestCase):
|
|||||||
last_param = {}
|
last_param = {}
|
||||||
|
|
||||||
vsa_id = 234
|
vsa_id = 234
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/%d' % vsa_id)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/%d' % vsa_id)
|
||||||
req.method = 'GET'
|
req.method = 'GET'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 404)
|
self.assertEqual(resp.status_int, 404)
|
||||||
self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
|
self.assertEqual(str(last_param['vsa_id']), str(vsa_id))
|
||||||
|
|
||||||
def test_vsa_index(self):
|
def test_vsa_index(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa')
|
req = webob.Request.blank('/v1.1/777/zadr-vsa')
|
||||||
req.method = 'GET'
|
req.method = 'GET'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
@@ -213,7 +212,7 @@ class VSAApiTest(test.TestCase):
|
|||||||
self.assertEqual(resp_vsa['id'], 123)
|
self.assertEqual(resp_vsa['id'], 123)
|
||||||
|
|
||||||
def test_vsa_detail(self):
|
def test_vsa_detail(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/detail')
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/detail')
|
||||||
req.method = 'GET'
|
req.method = 'GET'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
@@ -239,17 +238,21 @@ def _get_default_volume_param():
|
|||||||
'name': 'vol name',
|
'name': 'vol name',
|
||||||
'display_name': 'Default vol name',
|
'display_name': 'Default vol name',
|
||||||
'display_description': 'Default vol description',
|
'display_description': 'Default vol description',
|
||||||
'from_vsa_id': None,
|
'volume_type_id': 1,
|
||||||
'to_vsa_id': None,
|
'volume_metadata': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def stub_get_vsa_volume_type(self, context):
|
||||||
|
return {'id': 1,
|
||||||
|
'name': 'VSA volume type',
|
||||||
|
'extra_specs': {'type': 'vsa_volume'}}
|
||||||
|
|
||||||
|
|
||||||
def stub_volume_create(self, context, size, snapshot_id, name, description,
|
def stub_volume_create(self, context, size, snapshot_id, name, description,
|
||||||
**param):
|
**param):
|
||||||
LOG.debug(_("_create: param=%s"), size)
|
LOG.debug(_("_create: param=%s"), size)
|
||||||
vol = _get_default_volume_param()
|
vol = _get_default_volume_param()
|
||||||
for k, v in param.iteritems():
|
|
||||||
vol[k] = v
|
|
||||||
vol['size'] = size
|
vol['size'] = size
|
||||||
vol['display_name'] = name
|
vol['display_name'] = name
|
||||||
vol['display_description'] = description
|
vol['display_description'] = description
|
||||||
@@ -270,10 +273,10 @@ def stub_volume_get(self, context, volume_id):
|
|||||||
LOG.debug(_("_volume_get: volume_id=%s"), volume_id)
|
LOG.debug(_("_volume_get: volume_id=%s"), volume_id)
|
||||||
vol = _get_default_volume_param()
|
vol = _get_default_volume_param()
|
||||||
vol['id'] = volume_id
|
vol['id'] = volume_id
|
||||||
if volume_id == '234':
|
meta = {'key': 'from_vsa_id', 'value': '123'}
|
||||||
vol['from_vsa_id'] = 123
|
|
||||||
if volume_id == '345':
|
if volume_id == '345':
|
||||||
vol['to_vsa_id'] = 123
|
meta = {'key': 'to_vsa_id', 'value': '123'}
|
||||||
|
vol['volume_metadata'].append(meta)
|
||||||
return vol
|
return vol
|
||||||
|
|
||||||
|
|
||||||
@@ -281,9 +284,9 @@ def stub_volume_get_notfound(self, context, volume_id):
|
|||||||
raise exception.NotFound
|
raise exception.NotFound
|
||||||
|
|
||||||
|
|
||||||
def stub_volume_get_all_by_vsa(self, context, vsa_id, direction):
|
def stub_volume_get_all(self, context, search_opts):
|
||||||
vol = stub_volume_get(self, context, '123')
|
vol = stub_volume_get(self, context, '123')
|
||||||
vol['%s_vsa_id' % direction] = vsa_id
|
vol['metadata'] = search_opts['metadata']
|
||||||
return [vol]
|
return [vol]
|
||||||
|
|
||||||
|
|
||||||
@@ -302,13 +305,13 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
fakes.stub_out_rate_limiting(self.stubs)
|
fakes.stub_out_rate_limiting(self.stubs)
|
||||||
fakes.stub_out_auth(self.stubs)
|
fakes.stub_out_auth(self.stubs)
|
||||||
self.stubs.Set(nova.db.api, 'vsa_get', return_vsa)
|
self.stubs.Set(nova.db.api, '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, "create", stub_volume_create)
|
|
||||||
self.stubs.Set(volume.api.API, "update", stub_volume_update)
|
self.stubs.Set(volume.api.API, "update", stub_volume_update)
|
||||||
self.stubs.Set(volume.api.API, "delete", stub_volume_delete)
|
self.stubs.Set(volume.api.API, "delete", stub_volume_delete)
|
||||||
self.stubs.Set(volume.api.API, "get_all_by_vsa",
|
|
||||||
stub_volume_get_all_by_vsa)
|
|
||||||
self.stubs.Set(volume.api.API, "get", stub_volume_get)
|
self.stubs.Set(volume.api.API, "get", stub_volume_get)
|
||||||
|
self.stubs.Set(volume.api.API, "get_all", stub_volume_get_all)
|
||||||
|
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
self.test_obj = test_obj if test_obj else "volume"
|
self.test_obj = test_obj if test_obj else "volume"
|
||||||
@@ -319,11 +322,13 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
super(VSAVolumeApiTest, self).tearDown()
|
super(VSAVolumeApiTest, self).tearDown()
|
||||||
|
|
||||||
def test_vsa_volume_create(self):
|
def test_vsa_volume_create(self):
|
||||||
|
self.stubs.Set(volume.api.API, "create", stub_volume_create)
|
||||||
|
|
||||||
vol = {"size": 100,
|
vol = {"size": 100,
|
||||||
"displayName": "VSA Volume Test Name",
|
"displayName": "VSA Volume Test Name",
|
||||||
"displayDescription": "VSA Volume Test Desc"}
|
"displayDescription": "VSA Volume Test Desc"}
|
||||||
body = {self.test_obj: vol}
|
body = {self.test_obj: vol}
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s' % self.test_objs)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
|
||||||
req.method = 'POST'
|
req.method = 'POST'
|
||||||
req.body = json.dumps(body)
|
req.body = json.dumps(body)
|
||||||
req.headers['content-type'] = 'application/json'
|
req.headers['content-type'] = 'application/json'
|
||||||
@@ -344,7 +349,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
self.assertEqual(resp.status_int, 400)
|
self.assertEqual(resp.status_int, 400)
|
||||||
|
|
||||||
def test_vsa_volume_create_no_body(self):
|
def test_vsa_volume_create_no_body(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s' % self.test_objs)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
|
||||||
req.method = 'POST'
|
req.method = 'POST'
|
||||||
req.body = json.dumps({})
|
req.body = json.dumps({})
|
||||||
req.headers['content-type'] = 'application/json'
|
req.headers['content-type'] = 'application/json'
|
||||||
@@ -356,25 +361,25 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
self.assertEqual(resp.status_int, 400)
|
self.assertEqual(resp.status_int, 400)
|
||||||
|
|
||||||
def test_vsa_volume_index(self):
|
def test_vsa_volume_index(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s' % self.test_objs)
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s' % self.test_objs)
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
|
||||||
def test_vsa_volume_detail(self):
|
def test_vsa_volume_detail(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/detail' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/detail' % \
|
||||||
self.test_objs)
|
self.test_objs)
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
|
||||||
def test_vsa_volume_show(self):
|
def test_vsa_volume_show(self):
|
||||||
obj_num = 234 if self.test_objs == "volumes" else 345
|
obj_num = 234 if self.test_objs == "volumes" else 345
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/%s' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
|
||||||
(self.test_objs, obj_num))
|
(self.test_objs, obj_num))
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 200)
|
self.assertEqual(resp.status_int, 200)
|
||||||
|
|
||||||
def test_vsa_volume_show_no_vsa_assignment(self):
|
def test_vsa_volume_show_no_vsa_assignment(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/333' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \
|
||||||
(self.test_objs))
|
(self.test_objs))
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 400)
|
self.assertEqual(resp.status_int, 400)
|
||||||
@@ -382,7 +387,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
def test_vsa_volume_show_no_volume(self):
|
def test_vsa_volume_show_no_volume(self):
|
||||||
self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
|
self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
|
||||||
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/333' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \
|
||||||
(self.test_objs))
|
(self.test_objs))
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
self.assertEqual(resp.status_int, 404)
|
self.assertEqual(resp.status_int, 404)
|
||||||
@@ -392,7 +397,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
update = {"status": "available",
|
update = {"status": "available",
|
||||||
"displayName": "Test Display name"}
|
"displayName": "Test Display name"}
|
||||||
body = {self.test_obj: update}
|
body = {self.test_obj: update}
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/%s' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
|
||||||
(self.test_objs, obj_num))
|
(self.test_objs, obj_num))
|
||||||
req.method = 'PUT'
|
req.method = 'PUT'
|
||||||
req.body = json.dumps(body)
|
req.body = json.dumps(body)
|
||||||
@@ -406,7 +411,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
|
|
||||||
def test_vsa_volume_delete(self):
|
def test_vsa_volume_delete(self):
|
||||||
obj_num = 234 if self.test_objs == "volumes" else 345
|
obj_num = 234 if self.test_objs == "volumes" else 345
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/%s' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/%s' % \
|
||||||
(self.test_objs, obj_num))
|
(self.test_objs, obj_num))
|
||||||
req.method = 'DELETE'
|
req.method = 'DELETE'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
@@ -416,7 +421,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
self.assertEqual(resp.status_int, 400)
|
self.assertEqual(resp.status_int, 400)
|
||||||
|
|
||||||
def test_vsa_volume_delete_no_vsa_assignment(self):
|
def test_vsa_volume_delete_no_vsa_assignment(self):
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/333' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/4/%s/333' % \
|
||||||
(self.test_objs))
|
(self.test_objs))
|
||||||
req.method = 'DELETE'
|
req.method = 'DELETE'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
@@ -425,7 +430,7 @@ class VSAVolumeApiTest(test.TestCase):
|
|||||||
def test_vsa_volume_delete_no_volume(self):
|
def test_vsa_volume_delete_no_volume(self):
|
||||||
self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
|
self.stubs.Set(volume.api.API, "get", stub_volume_get_notfound)
|
||||||
|
|
||||||
req = webob.Request.blank('/v1.1/zadr-vsa/123/%s/333' % \
|
req = webob.Request.blank('/v1.1/777/zadr-vsa/123/%s/333' % \
|
||||||
(self.test_objs))
|
(self.test_objs))
|
||||||
req.method = 'DELETE'
|
req.method = 'DELETE'
|
||||||
resp = req.get_response(fakes.wsgi_app())
|
resp = req.get_response(fakes.wsgi_app())
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class ExtensionControllerTest(test.TestCase):
|
|||||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||||
self.flags(osapi_extensions_path=ext_path)
|
self.flags(osapi_extensions_path=ext_path)
|
||||||
self.ext_list = [
|
self.ext_list = [
|
||||||
"DriveTypes",
|
|
||||||
"Createserverext",
|
"Createserverext",
|
||||||
"FlavorExtraSpecs",
|
"FlavorExtraSpecs",
|
||||||
"Floating_ips",
|
"Floating_ips",
|
||||||
@@ -96,8 +95,8 @@ class ExtensionControllerTest(test.TestCase):
|
|||||||
"Quotas",
|
"Quotas",
|
||||||
"Rescue",
|
"Rescue",
|
||||||
"SecurityGroups",
|
"SecurityGroups",
|
||||||
"VirtualInterfaces",
|
|
||||||
"VSAs",
|
"VSAs",
|
||||||
|
"VirtualInterfaces",
|
||||||
"Volumes",
|
"Volumes",
|
||||||
"VolumeTypes",
|
"VolumeTypes",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -16,13 +16,15 @@
|
|||||||
import stubout
|
import stubout
|
||||||
|
|
||||||
import nova
|
import nova
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import db
|
from nova import log as logging
|
||||||
from nova import context
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import log as logging
|
from nova.volume import volume_types
|
||||||
|
|
||||||
from nova.scheduler import vsa as vsa_sched
|
from nova.scheduler import vsa as vsa_sched
|
||||||
from nova.scheduler import driver
|
from nova.scheduler import driver
|
||||||
@@ -52,15 +54,26 @@ class VsaSchedulerTestCase(test.TestCase):
|
|||||||
def _get_vol_creation_request(self, num_vols, drive_ix, size=0):
|
def _get_vol_creation_request(self, num_vols, drive_ix, size=0):
|
||||||
volume_params = []
|
volume_params = []
|
||||||
for i in range(num_vols):
|
for i in range(num_vols):
|
||||||
drive_type = {'id': i,
|
|
||||||
'name': 'name_' + str(drive_ix),
|
name = 'name_' + str(i)
|
||||||
'type': 'type_' + str(drive_ix),
|
try:
|
||||||
'size_gb': 1 + 100 * (drive_ix)}
|
volume_types.create(self.context, 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.ApiError:
|
||||||
|
# type is already created
|
||||||
|
pass
|
||||||
|
|
||||||
|
volume_type = volume_types.get_volume_type_by_name(self.context,
|
||||||
|
name)
|
||||||
volume = {'size': size,
|
volume = {'size': size,
|
||||||
'snapshot_id': None,
|
'snapshot_id': None,
|
||||||
'name': 'vol_' + str(i),
|
'name': 'vol_' + str(i),
|
||||||
'description': None,
|
'description': None,
|
||||||
'drive_ref': drive_type}
|
'volume_type_id': volume_type['id']}
|
||||||
volume_params.append(volume)
|
volume_params.append(volume)
|
||||||
|
|
||||||
return {'num_volumes': len(volume_params),
|
return {'num_volumes': len(volume_params),
|
||||||
@@ -217,7 +230,12 @@ class VsaSchedulerTestCase(test.TestCase):
|
|||||||
self.stubs.Set(nova.db, 'volume_get', self._fake_volume_get)
|
self.stubs.Set(nova.db, 'volume_get', self._fake_volume_get)
|
||||||
self.stubs.Set(nova.db, 'volume_update', self._fake_volume_update)
|
self.stubs.Set(nova.db, 'volume_update', self._fake_volume_update)
|
||||||
|
|
||||||
|
self.created_types_lst = []
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
for name in self.created_types_lst:
|
||||||
|
volume_types.purge(self.context, name)
|
||||||
|
|
||||||
self.stubs.UnsetAll()
|
self.stubs.UnsetAll()
|
||||||
super(VsaSchedulerTestCase, self).tearDown()
|
super(VsaSchedulerTestCase, self).tearDown()
|
||||||
|
|
||||||
@@ -463,7 +481,7 @@ class VsaSchedulerTestCase(test.TestCase):
|
|||||||
|
|
||||||
global global_volume
|
global global_volume
|
||||||
global_volume = {}
|
global_volume = {}
|
||||||
global_volume['drive_type'] = None
|
global_volume['volume_type_id'] = None
|
||||||
|
|
||||||
self.assertRaises(driver.NoValidHost,
|
self.assertRaises(driver.NoValidHost,
|
||||||
self.sched.schedule_create_volume,
|
self.sched.schedule_create_volume,
|
||||||
@@ -485,12 +503,16 @@ class VsaSchedulerTestCase(test.TestCase):
|
|||||||
global_volume = {}
|
global_volume = {}
|
||||||
|
|
||||||
drive_ix = 2
|
drive_ix = 2
|
||||||
drive_type = {'id': drive_ix,
|
name = 'name_' + str(drive_ix)
|
||||||
'name': 'name_' + str(drive_ix),
|
volume_types.create(self.context, name,
|
||||||
'type': 'type_' + str(drive_ix),
|
extra_specs={'type': 'vsa_drive',
|
||||||
'size_gb': 1 + 100 * (drive_ix)}
|
'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['drive_type'] = drive_type
|
global_volume['volume_type_id'] = volume_type['id']
|
||||||
global_volume['size'] = 0
|
global_volume['size'] = 0
|
||||||
|
|
||||||
host = self.sched.schedule_create_volume(self.context,
|
host = self.sched.schedule_create_volume(self.context,
|
||||||
@@ -525,12 +547,16 @@ class VsaSchedulerTestCaseMostAvail(VsaSchedulerTestCase):
|
|||||||
global_volume = {}
|
global_volume = {}
|
||||||
|
|
||||||
drive_ix = 2
|
drive_ix = 2
|
||||||
drive_type = {'id': drive_ix,
|
name = 'name_' + str(drive_ix)
|
||||||
'name': 'name_' + str(drive_ix),
|
volume_types.create(self.context, name,
|
||||||
'type': 'type_' + str(drive_ix),
|
extra_specs={'type': 'vsa_drive',
|
||||||
'size_gb': 1 + 100 * (drive_ix)}
|
'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['drive_type'] = drive_type
|
global_volume['volume_type_id'] = volume_type['id']
|
||||||
global_volume['size'] = 0
|
global_volume['size'] = 0
|
||||||
|
|
||||||
host = self.sched.schedule_create_volume(self.context,
|
host = self.sched.schedule_create_volume(self.context,
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright (c) 2011 Zadara Storage Inc.
|
|
||||||
# Copyright (c) 2011 OpenStack LLC.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Unit Tests for drive types codecode
|
|
||||||
"""
|
|
||||||
import time
|
|
||||||
|
|
||||||
from nova import context
|
|
||||||
from nova import flags
|
|
||||||
from nova import log as logging
|
|
||||||
from nova import test
|
|
||||||
from nova.vsa import drive_types
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
LOG = logging.getLogger('nova.tests.test_drive_types')
|
|
||||||
|
|
||||||
|
|
||||||
class DriveTypesTestCase(test.TestCase):
|
|
||||||
"""Test cases for driver types code"""
|
|
||||||
def setUp(self):
|
|
||||||
super(DriveTypesTestCase, self).setUp()
|
|
||||||
self.ctxt = context.RequestContext(None, None)
|
|
||||||
self.ctxt_admin = context.get_admin_context()
|
|
||||||
self._dtype = self._create_default_drive_type()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self._dtype = None
|
|
||||||
|
|
||||||
def _create_default_drive_type(self):
|
|
||||||
"""Create a volume object."""
|
|
||||||
dtype = {}
|
|
||||||
dtype['type'] = 'SATA'
|
|
||||||
dtype['size_gb'] = 150
|
|
||||||
dtype['rpm'] = 5000
|
|
||||||
dtype['capabilities'] = None
|
|
||||||
dtype['visible'] = True
|
|
||||||
|
|
||||||
LOG.debug(_("Default values for Drive Type: %s"), dtype)
|
|
||||||
return dtype
|
|
||||||
|
|
||||||
def test_drive_type_create_delete(self):
|
|
||||||
dtype = self._dtype
|
|
||||||
prev_all_dtypes = drive_types.get_all(self.ctxt_admin, False)
|
|
||||||
|
|
||||||
new = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
for k, v in dtype.iteritems():
|
|
||||||
self.assertEqual(v, new[k], 'one of fields doesnt match')
|
|
||||||
|
|
||||||
new_all_dtypes = drive_types.get_all(self.ctxt_admin, False)
|
|
||||||
self.assertNotEqual(len(prev_all_dtypes),
|
|
||||||
len(new_all_dtypes),
|
|
||||||
'drive type was not created')
|
|
||||||
|
|
||||||
drive_types.delete(self.ctxt_admin, new['id'])
|
|
||||||
new_all_dtypes = drive_types.get_all(self.ctxt_admin, False)
|
|
||||||
self.assertEqual(prev_all_dtypes,
|
|
||||||
new_all_dtypes,
|
|
||||||
'drive types was not deleted')
|
|
||||||
|
|
||||||
def test_drive_type_check_name_generation(self):
|
|
||||||
dtype = self._dtype
|
|
||||||
new = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
expected_name = FLAGS.drive_type_template_short % \
|
|
||||||
(dtype['type'], dtype['size_gb'], dtype['rpm'])
|
|
||||||
self.assertEqual(new['name'], expected_name,
|
|
||||||
'name was not generated correctly')
|
|
||||||
|
|
||||||
dtype['capabilities'] = 'SEC'
|
|
||||||
new2 = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
expected_name = FLAGS.drive_type_template_long % \
|
|
||||||
(dtype['type'], dtype['size_gb'], dtype['rpm'],
|
|
||||||
dtype['capabilities'])
|
|
||||||
self.assertEqual(new2['name'], expected_name,
|
|
||||||
'name was not generated correctly')
|
|
||||||
|
|
||||||
drive_types.delete(self.ctxt_admin, new['id'])
|
|
||||||
drive_types.delete(self.ctxt_admin, new2['id'])
|
|
||||||
|
|
||||||
def test_drive_type_create_delete_invisible(self):
|
|
||||||
dtype = self._dtype
|
|
||||||
dtype['visible'] = False
|
|
||||||
prev_all_dtypes = drive_types.get_all(self.ctxt_admin, True)
|
|
||||||
new = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
|
|
||||||
new_all_dtypes = drive_types.get_all(self.ctxt_admin, True)
|
|
||||||
self.assertEqual(prev_all_dtypes, new_all_dtypes)
|
|
||||||
|
|
||||||
new_all_dtypes = drive_types.get_all(self.ctxt_admin, False)
|
|
||||||
self.assertNotEqual(prev_all_dtypes, new_all_dtypes)
|
|
||||||
|
|
||||||
drive_types.delete(self.ctxt_admin, new['id'])
|
|
||||||
|
|
||||||
def test_drive_type_rename_update(self):
|
|
||||||
dtype = self._dtype
|
|
||||||
dtype['capabilities'] = None
|
|
||||||
|
|
||||||
new = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
for k, v in dtype.iteritems():
|
|
||||||
self.assertEqual(v, new[k], 'one of fields doesnt match')
|
|
||||||
|
|
||||||
new_name = 'NEW_DRIVE_NAME'
|
|
||||||
new = drive_types.rename(self.ctxt_admin, new['name'], new_name)
|
|
||||||
self.assertEqual(new['name'], new_name)
|
|
||||||
|
|
||||||
new = drive_types.rename(self.ctxt_admin, new_name)
|
|
||||||
expected_name = FLAGS.drive_type_template_short % \
|
|
||||||
(dtype['type'], dtype['size_gb'], dtype['rpm'])
|
|
||||||
self.assertEqual(new['name'], expected_name)
|
|
||||||
|
|
||||||
changes = {'rpm': 7200}
|
|
||||||
new = drive_types.update(self.ctxt_admin, new['id'], **changes)
|
|
||||||
for k, v in changes.iteritems():
|
|
||||||
self.assertEqual(v, new[k], 'one of fields doesnt match')
|
|
||||||
|
|
||||||
drive_types.delete(self.ctxt_admin, new['id'])
|
|
||||||
|
|
||||||
def test_drive_type_get(self):
|
|
||||||
dtype = self._dtype
|
|
||||||
new = drive_types.create(self.ctxt_admin, **dtype)
|
|
||||||
|
|
||||||
new2 = drive_types.get(self.ctxt_admin, new['id'])
|
|
||||||
for k, v in new2.iteritems():
|
|
||||||
self.assertEqual(str(new[k]), str(new2[k]),
|
|
||||||
'one of fields doesnt match')
|
|
||||||
|
|
||||||
new2 = drive_types.get_by_name(self.ctxt_admin, new['name'])
|
|
||||||
for k, v in new.iteritems():
|
|
||||||
self.assertEqual(str(new[k]), str(new2[k]),
|
|
||||||
'one of fields doesnt match')
|
|
||||||
|
|
||||||
drive_types.delete(self.ctxt_admin, new['id'])
|
|
||||||
@@ -13,38 +13,29 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import stubout
|
|
||||||
import base64
|
import base64
|
||||||
|
import stubout
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
from xml.etree.ElementTree import Element, SubElement
|
from xml.etree.ElementTree import Element, SubElement
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import test
|
||||||
from nova import vsa
|
from nova import vsa
|
||||||
from nova import volume
|
from nova import volume
|
||||||
from nova import db
|
from nova.volume import volume_types
|
||||||
from nova import context
|
from nova.vsa import utils as vsa_utils
|
||||||
from nova import test
|
|
||||||
from nova import log as logging
|
|
||||||
import nova.image.fake
|
import nova.image.fake
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
LOG = logging.getLogger('nova.tests.vsa')
|
LOG = logging.getLogger('nova.tests.vsa')
|
||||||
|
|
||||||
|
|
||||||
def fake_drive_type_get_by_name(context, name):
|
|
||||||
drive_type = {
|
|
||||||
'id': 1,
|
|
||||||
'name': name,
|
|
||||||
'type': name.split('_')[0],
|
|
||||||
'size_gb': int(name.split('_')[1]),
|
|
||||||
'rpm': name.split('_')[2],
|
|
||||||
'capabilities': '',
|
|
||||||
'visible': True}
|
|
||||||
return drive_type
|
|
||||||
|
|
||||||
|
|
||||||
class VsaTestCase(test.TestCase):
|
class VsaTestCase(test.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -53,9 +44,20 @@ class VsaTestCase(test.TestCase):
|
|||||||
self.vsa_api = vsa.API()
|
self.vsa_api = vsa.API()
|
||||||
self.volume_api = volume.API()
|
self.volume_api = volume.API()
|
||||||
|
|
||||||
|
FLAGS.quota_volumes = 100
|
||||||
|
FLAGS.quota_gigabytes = 10000
|
||||||
|
|
||||||
self.context_non_admin = context.RequestContext(None, None)
|
self.context_non_admin = context.RequestContext(None, None)
|
||||||
self.context = context.get_admin_context()
|
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):
|
def fake_show_by_name(meh, context, name):
|
||||||
if name == 'wrong_image_name':
|
if name == 'wrong_image_name':
|
||||||
LOG.debug(_("Test: Emulate wrong VSA name. Raise"))
|
LOG.debug(_("Test: Emulate wrong VSA name. Raise"))
|
||||||
@@ -124,9 +126,6 @@ class VsaTestCase(test.TestCase):
|
|||||||
|
|
||||||
FLAGS.vsa_multi_vol_creation = multi_vol_creation
|
FLAGS.vsa_multi_vol_creation = multi_vol_creation
|
||||||
|
|
||||||
self.stubs.Set(nova.vsa.drive_types, 'get_by_name',
|
|
||||||
fake_drive_type_get_by_name)
|
|
||||||
|
|
||||||
param = {'storage': [{'drive_name': 'SATA_500_7200',
|
param = {'storage': [{'drive_name': 'SATA_500_7200',
|
||||||
'num_drives': 3}]}
|
'num_drives': 3}]}
|
||||||
vsa_ref = self.vsa_api.create(self.context, **param)
|
vsa_ref = self.vsa_api.create(self.context, **param)
|
||||||
@@ -157,8 +156,6 @@ class VsaTestCase(test.TestCase):
|
|||||||
self.vsa_api.delete(self.context, vsa_ref['id'])
|
self.vsa_api.delete(self.context, vsa_ref['id'])
|
||||||
|
|
||||||
def test_vsa_generate_user_data(self):
|
def test_vsa_generate_user_data(self):
|
||||||
self.stubs.Set(nova.vsa.drive_types, 'get_by_name',
|
|
||||||
fake_drive_type_get_by_name)
|
|
||||||
|
|
||||||
FLAGS.vsa_multi_vol_creation = False
|
FLAGS.vsa_multi_vol_creation = False
|
||||||
param = {'display_name': 'VSA name test',
|
param = {'display_name': 'VSA name test',
|
||||||
@@ -167,12 +164,10 @@ class VsaTestCase(test.TestCase):
|
|||||||
'storage': [{'drive_name': 'SATA_500_7200',
|
'storage': [{'drive_name': 'SATA_500_7200',
|
||||||
'num_drives': 3}]}
|
'num_drives': 3}]}
|
||||||
vsa_ref = self.vsa_api.create(self.context, **param)
|
vsa_ref = self.vsa_api.create(self.context, **param)
|
||||||
volumes = db.volume_get_all_assigned_to_vsa(self.context,
|
volumes = self.vsa_api.get_all_vsa_drives(self.context,
|
||||||
vsa_ref['id'])
|
vsa_ref['id'])
|
||||||
|
|
||||||
user_data = self.vsa_api.generate_user_data(self.context,
|
user_data = vsa_utils.generate_user_data(vsa_ref, volumes)
|
||||||
vsa_ref,
|
|
||||||
volumes)
|
|
||||||
user_data = base64.b64decode(user_data)
|
user_data = base64.b64decode(user_data)
|
||||||
|
|
||||||
LOG.debug(_("Test: user_data = %s"), user_data)
|
LOG.debug(_("Test: user_data = %s"), user_data)
|
||||||
|
|||||||
@@ -29,15 +29,6 @@ FLAGS = flags.FLAGS
|
|||||||
LOG = logging.getLogger('nova.tests.vsa.volumes')
|
LOG = logging.getLogger('nova.tests.vsa.volumes')
|
||||||
|
|
||||||
|
|
||||||
def _default_volume_param():
|
|
||||||
return {
|
|
||||||
'size': 1,
|
|
||||||
'snapshot_id': None,
|
|
||||||
'name': 'Test volume name',
|
|
||||||
'description': 'Test volume desc name'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VsaVolumesTestCase(test.TestCase):
|
class VsaVolumesTestCase(test.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -49,6 +40,8 @@ class VsaVolumesTestCase(test.TestCase):
|
|||||||
self.context_non_admin = context.RequestContext(None, None)
|
self.context_non_admin = context.RequestContext(None, None)
|
||||||
self.context = context.get_admin_context()
|
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):
|
def fake_show_by_name(meh, context, name):
|
||||||
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
|
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
|
||||||
|
|
||||||
@@ -66,12 +59,23 @@ class VsaVolumesTestCase(test.TestCase):
|
|||||||
self.stubs.UnsetAll()
|
self.stubs.UnsetAll()
|
||||||
super(VsaVolumesTestCase, self).tearDown()
|
super(VsaVolumesTestCase, self).tearDown()
|
||||||
|
|
||||||
|
def _default_volume_param(self):
|
||||||
|
return {
|
||||||
|
'size': 1,
|
||||||
|
'snapshot_id': 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):
|
def test_vsa_volume_create_delete(self):
|
||||||
""" Check if volume properly created and deleted. """
|
""" Check if volume properly created and deleted. """
|
||||||
vols1 = self.volume_api.get_all_by_vsa(self.context,
|
volume_param = self._default_volume_param()
|
||||||
self.vsa_id, "from")
|
|
||||||
volume_param = _default_volume_param()
|
|
||||||
volume_param['from_vsa_id'] = self.vsa_id
|
|
||||||
volume_ref = self.volume_api.create(self.context, **volume_param)
|
volume_ref = self.volume_api.create(self.context, **volume_param)
|
||||||
|
|
||||||
self.assertEqual(volume_ref['display_name'],
|
self.assertEqual(volume_ref['display_name'],
|
||||||
@@ -81,21 +85,34 @@ class VsaVolumesTestCase(test.TestCase):
|
|||||||
self.assertEqual(volume_ref['size'],
|
self.assertEqual(volume_ref['size'],
|
||||||
volume_param['size'])
|
volume_param['size'])
|
||||||
self.assertEqual(volume_ref['status'],
|
self.assertEqual(volume_ref['status'],
|
||||||
'available')
|
'creating')
|
||||||
|
|
||||||
vols2 = self.volume_api.get_all_by_vsa(self.context,
|
vols2 = self._get_all_volumes_by_vsa()
|
||||||
self.vsa_id, "from")
|
self.assertEqual(1, len(vols2))
|
||||||
self.assertEqual(len(vols1) + 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['id'], {'status': 'available'})
|
||||||
self.volume_api.delete(self.context, volume_ref['id'])
|
self.volume_api.delete(self.context, volume_ref['id'])
|
||||||
vols3 = self.volume_api.get_all_by_vsa(self.context,
|
|
||||||
self.vsa_id, "from")
|
vols3 = self._get_all_volumes_by_vsa()
|
||||||
self.assertEqual(len(vols3) + 1, len(vols2))
|
self.assertEqual(1, len(vols2))
|
||||||
|
volume_ref = vols3[0]
|
||||||
|
self.assertEqual(volume_ref['status'],
|
||||||
|
'deleting')
|
||||||
|
|
||||||
def test_vsa_volume_delete_nonavail_volume(self):
|
def test_vsa_volume_delete_nonavail_volume(self):
|
||||||
""" Check volume deleton in different states. """
|
""" Check volume deleton in different states. """
|
||||||
volume_param = _default_volume_param()
|
volume_param = self._default_volume_param()
|
||||||
volume_param['from_vsa_id'] = self.vsa_id
|
|
||||||
volume_ref = self.volume_api.create(self.context, **volume_param)
|
volume_ref = self.volume_api.create(self.context, **volume_param)
|
||||||
|
|
||||||
self.volume_api.update(self.context,
|
self.volume_api.update(self.context,
|
||||||
@@ -104,26 +121,18 @@ class VsaVolumesTestCase(test.TestCase):
|
|||||||
self.volume_api.delete,
|
self.volume_api.delete,
|
||||||
self.context, volume_ref['id'])
|
self.context, volume_ref['id'])
|
||||||
|
|
||||||
self.volume_api.update(self.context,
|
|
||||||
volume_ref['id'], {'status': 'error'})
|
|
||||||
self.volume_api.delete(self.context, volume_ref['id'])
|
|
||||||
|
|
||||||
def test_vsa_volume_delete_vsa_with_volumes(self):
|
def test_vsa_volume_delete_vsa_with_volumes(self):
|
||||||
""" Check volume deleton in different states. """
|
""" Check volume deleton in different states. """
|
||||||
|
|
||||||
vols1 = self.volume_api.get_all_by_vsa(self.context,
|
vols1 = self._get_all_volumes_by_vsa()
|
||||||
self.vsa_id, "from")
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
volume_param = _default_volume_param()
|
volume_param = self._default_volume_param()
|
||||||
volume_param['from_vsa_id'] = self.vsa_id
|
|
||||||
volume_ref = self.volume_api.create(self.context, **volume_param)
|
volume_ref = self.volume_api.create(self.context, **volume_param)
|
||||||
|
|
||||||
vols2 = self.volume_api.get_all_by_vsa(self.context,
|
vols2 = self._get_all_volumes_by_vsa()
|
||||||
self.vsa_id, "from")
|
|
||||||
self.assertEqual(len(vols1) + 3, len(vols2))
|
self.assertEqual(len(vols1) + 3, len(vols2))
|
||||||
|
|
||||||
self.vsa_api.delete(self.context, self.vsa_id)
|
self.vsa_api.delete(self.context, self.vsa_id)
|
||||||
|
|
||||||
vols3 = self.volume_api.get_all_by_vsa(self.context,
|
vols3 = self._get_all_volumes_by_vsa()
|
||||||
self.vsa_id, "from")
|
|
||||||
self.assertEqual(len(vols1), len(vols3))
|
self.assertEqual(len(vols1), len(vols3))
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||||
self.conn = xenapi_conn.get_connection(False)
|
self.conn = xenapi_conn.get_connection(False)
|
||||||
|
|
||||||
|
@test.skip_test("Skip this test meanwhile")
|
||||||
def test_parallel_builds(self):
|
def test_parallel_builds(self):
|
||||||
stubs.stubout_loopingcall_delay(self.stubs)
|
stubs.stubout_loopingcall_delay(self.stubs)
|
||||||
|
|
||||||
|
|||||||
@@ -135,8 +135,6 @@ flags.DEFINE_string('default_local_format',
|
|||||||
None,
|
None,
|
||||||
'The default format a local_volume will be formatted with '
|
'The default format a local_volume will be formatted with '
|
||||||
'on creation.')
|
'on creation.')
|
||||||
|
|
||||||
|
|
||||||
flags.DEFINE_bool('libvirt_use_virtio_for_bridges',
|
flags.DEFINE_bool('libvirt_use_virtio_for_bridges',
|
||||||
False,
|
False,
|
||||||
'Use virtio for bridge interfaces')
|
'Use virtio for bridge interfaces')
|
||||||
@@ -1088,7 +1086,8 @@ class LibvirtConnection(driver.ComputeDriver):
|
|||||||
'ebs_root': ebs_root,
|
'ebs_root': ebs_root,
|
||||||
'local_device': local_device,
|
'local_device': local_device,
|
||||||
'volumes': block_device_mapping,
|
'volumes': block_device_mapping,
|
||||||
'use_virtio_for_bridges': FLAGS.libvirt_use_virtio_for_bridges,
|
'use_virtio_for_bridges':
|
||||||
|
FLAGS.libvirt_use_virtio_for_bridges,
|
||||||
'ephemerals': ephemerals}
|
'ephemerals': ephemerals}
|
||||||
|
|
||||||
root_device_name = driver.block_device_info_get_root(block_device_info)
|
root_device_name = driver.block_device_info_get_root(block_device_info)
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ class API(base.Base):
|
|||||||
"""API for interacting with the volume manager."""
|
"""API for interacting with the volume manager."""
|
||||||
|
|
||||||
def create(self, context, size, snapshot_id, name, description,
|
def create(self, context, size, snapshot_id, name, description,
|
||||||
volume_type=None, metadata=None,
|
volume_type=None, metadata=None, availability_zone=None):
|
||||||
to_vsa_id=None, from_vsa_id=None, drive_type_id=None,
|
|
||||||
availability_zone=None):
|
|
||||||
if snapshot_id != None:
|
if snapshot_id != None:
|
||||||
snapshot = self.get_snapshot(context, snapshot_id)
|
snapshot = self.get_snapshot(context, snapshot_id)
|
||||||
if snapshot['status'] != "available":
|
if snapshot['status'] != "available":
|
||||||
@@ -53,7 +51,6 @@ class API(base.Base):
|
|||||||
if not size:
|
if not size:
|
||||||
size = snapshot['volume_size']
|
size = snapshot['volume_size']
|
||||||
|
|
||||||
if to_vsa_id is None:
|
|
||||||
if quota.allowed_volumes(context, 1, size) < 1:
|
if quota.allowed_volumes(context, 1, size) < 1:
|
||||||
pid = context.project_id
|
pid = context.project_id
|
||||||
LOG.warn(_("Quota exceeded for %(pid)s, tried to create"
|
LOG.warn(_("Quota exceeded for %(pid)s, tried to create"
|
||||||
@@ -81,19 +78,9 @@ class API(base.Base):
|
|||||||
'display_description': description,
|
'display_description': description,
|
||||||
'volume_type_id': volume_type_id,
|
'volume_type_id': volume_type_id,
|
||||||
'metadata': metadata,
|
'metadata': metadata,
|
||||||
'to_vsa_id': to_vsa_id,
|
|
||||||
'from_vsa_id': from_vsa_id,
|
|
||||||
'drive_type_id': drive_type_id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
volume = self.db.volume_create(context, options)
|
volume = self.db.volume_create(context, options)
|
||||||
if from_vsa_id is not None: # for FE VSA volumes do nothing
|
|
||||||
now = utils.utcnow()
|
|
||||||
volume = self.db.volume_update(context,
|
|
||||||
volume['id'], {'status': 'available',
|
|
||||||
'launched_at': now})
|
|
||||||
return volume
|
|
||||||
|
|
||||||
rpc.cast(context,
|
rpc.cast(context,
|
||||||
FLAGS.scheduler_topic,
|
FLAGS.scheduler_topic,
|
||||||
{"method": "create_volume",
|
{"method": "create_volume",
|
||||||
@@ -112,15 +99,6 @@ class API(base.Base):
|
|||||||
|
|
||||||
def delete(self, context, volume_id):
|
def delete(self, context, volume_id):
|
||||||
volume = self.get(context, volume_id)
|
volume = self.get(context, volume_id)
|
||||||
|
|
||||||
if volume['from_vsa_id'] is not None:
|
|
||||||
if volume['status'] == "in-use":
|
|
||||||
raise exception.ApiError(_("Volume is in use. "\
|
|
||||||
"Detach it first"))
|
|
||||||
self.db.volume_destroy(context, volume['id'])
|
|
||||||
LOG.debug(_("volume %d: deleted successfully"), volume['id'])
|
|
||||||
return
|
|
||||||
|
|
||||||
if volume['status'] != "available":
|
if volume['status'] != "available":
|
||||||
raise exception.ApiError(_("Volume status must be available"))
|
raise exception.ApiError(_("Volume status must be available"))
|
||||||
now = utils.utcnow()
|
now = utils.utcnow()
|
||||||
@@ -154,7 +132,7 @@ class API(base.Base):
|
|||||||
for i in volume.get('volume_metadata'):
|
for i in volume.get('volume_metadata'):
|
||||||
volume_metadata[i['key']] = i['value']
|
volume_metadata[i['key']] = i['value']
|
||||||
|
|
||||||
for k, v in searchdict:
|
for k, v in searchdict.iteritems():
|
||||||
if k not in volume_metadata.keys()\
|
if k not in volume_metadata.keys()\
|
||||||
or volume_metadata[k] != v:
|
or volume_metadata[k] != v:
|
||||||
return False
|
return False
|
||||||
@@ -163,6 +141,7 @@ class API(base.Base):
|
|||||||
# search_option to filter_name mapping.
|
# search_option to filter_name mapping.
|
||||||
filter_mapping = {'metadata': _check_metadata_match}
|
filter_mapping = {'metadata': _check_metadata_match}
|
||||||
|
|
||||||
|
result = []
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
# go over all filters in the list
|
# go over all filters in the list
|
||||||
for opt, values in search_opts.iteritems():
|
for opt, values in search_opts.iteritems():
|
||||||
@@ -172,21 +151,12 @@ class API(base.Base):
|
|||||||
# no such filter - ignore it, go to next filter
|
# no such filter - ignore it, go to next filter
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if filter_func(volume, values) == False:
|
if filter_func(volume, values):
|
||||||
# if one of conditions didn't match - remove
|
result.append(volume)
|
||||||
volumes.remove(volume)
|
|
||||||
break
|
break
|
||||||
|
volumes = result
|
||||||
return volumes
|
return volumes
|
||||||
|
|
||||||
def get_all_by_vsa(self, context, vsa_id, direction):
|
|
||||||
if direction == "to":
|
|
||||||
return self.db.volume_get_all_assigned_to_vsa(context, vsa_id)
|
|
||||||
elif direction == "from":
|
|
||||||
return self.db.volume_get_all_assigned_from_vsa(context, vsa_id)
|
|
||||||
else:
|
|
||||||
raise exception.ApiError(_("Unsupported vol assignment type %s"),
|
|
||||||
direction)
|
|
||||||
|
|
||||||
def get_snapshot(self, context, snapshot_id):
|
def get_snapshot(self, context, snapshot_id):
|
||||||
rv = self.db.snapshot_get(context, snapshot_id)
|
rv = self.db.snapshot_get(context, snapshot_id)
|
||||||
return dict(rv.iteritems())
|
return dict(rv.iteritems())
|
||||||
@@ -286,3 +256,12 @@ class API(base.Base):
|
|||||||
|
|
||||||
self.db.volume_metadata_update(context, volume_id, _metadata, True)
|
self.db.volume_metadata_update(context, volume_id, _metadata, True)
|
||||||
return _metadata
|
return _metadata
|
||||||
|
|
||||||
|
def get_volume_metadata_value(self, volume, key):
|
||||||
|
"""Get value of particular metadata key."""
|
||||||
|
metadata = volume.get('volume_metadata')
|
||||||
|
if metadata:
|
||||||
|
for i in volume['volume_metadata']:
|
||||||
|
if i['key'] == key:
|
||||||
|
return i['value']
|
||||||
|
return None
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ from nova import exception
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova.volume import volume_types
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger("nova.volume.driver")
|
LOG = logging.getLogger("nova.volume.driver")
|
||||||
@@ -516,7 +517,7 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
iscsi_properties = self._get_iscsi_properties(volume)
|
iscsi_properties = self._get_iscsi_properties(volume)
|
||||||
|
|
||||||
if not iscsi_properties['target_discovered']:
|
if not iscsi_properties['target_discovered']:
|
||||||
self._run_iscsiadm(iscsi_properties, '--op=new')
|
self._run_iscsiadm(iscsi_properties, ('--op', 'new'))
|
||||||
|
|
||||||
if iscsi_properties.get('auth_method'):
|
if iscsi_properties.get('auth_method'):
|
||||||
self._iscsiadm_update(iscsi_properties,
|
self._iscsiadm_update(iscsi_properties,
|
||||||
@@ -568,7 +569,7 @@ class ISCSIDriver(VolumeDriver):
|
|||||||
iscsi_properties = self._get_iscsi_properties(volume)
|
iscsi_properties = self._get_iscsi_properties(volume)
|
||||||
self._iscsiadm_update(iscsi_properties, "node.startup", "manual")
|
self._iscsiadm_update(iscsi_properties, "node.startup", "manual")
|
||||||
self._run_iscsiadm(iscsi_properties, "--logout")
|
self._run_iscsiadm(iscsi_properties, "--logout")
|
||||||
self._run_iscsiadm(iscsi_properties, '--op=delete')
|
self._run_iscsiadm(iscsi_properties, ('--op', 'delete'))
|
||||||
|
|
||||||
def check_for_export(self, context, volume_id):
|
def check_for_export(self, context, volume_id):
|
||||||
"""Make sure volume is exported."""
|
"""Make sure volume is exported."""
|
||||||
@@ -813,9 +814,15 @@ class LoggingVolumeDriver(VolumeDriver):
|
|||||||
class ZadaraBEDriver(ISCSIDriver):
|
class ZadaraBEDriver(ISCSIDriver):
|
||||||
"""Performs actions to configure Zadara BE module."""
|
"""Performs actions to configure Zadara BE module."""
|
||||||
|
|
||||||
def _not_vsa_be_volume(self, volume):
|
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."""
|
"""Returns True if volume is not VSA BE volume."""
|
||||||
if volume['to_vsa_id'] is None:
|
if not volume_types.is_vsa_object(volume['volume_type_id']):
|
||||||
LOG.debug(_("\tVolume %s is NOT VSA volume"), volume['name'])
|
LOG.debug(_("\tVolume %s is NOT VSA volume"), volume['name'])
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -828,9 +835,14 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
""" Volume Driver methods """
|
""" Volume Driver methods """
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
"""Creates BE volume."""
|
"""Creates BE volume."""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).create_volume(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:
|
if int(volume['size']) == 0:
|
||||||
sizestr = '0' # indicates full-partition
|
sizestr = '0' # indicates full-partition
|
||||||
else:
|
else:
|
||||||
@@ -838,9 +850,16 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
|
|
||||||
# Set the qos-str to default type sas
|
# Set the qos-str to default type sas
|
||||||
qosstr = 'SAS_1000'
|
qosstr = 'SAS_1000'
|
||||||
drive_type = volume.get('drive_type')
|
LOG.debug(_("\tvolume_type_id=%s"), volume['volume_type_id'])
|
||||||
if drive_type is not None:
|
|
||||||
qosstr = drive_type['type'] + ("_%s" % drive_type['size_gb'])
|
volume_type = volume_types.get_volume_type(None,
|
||||||
|
volume['volume_type_id'])
|
||||||
|
|
||||||
|
LOG.debug(_("\tvolume_type=%s"), volume_type)
|
||||||
|
|
||||||
|
if volume_type is not None:
|
||||||
|
qosstr = volume_type['extra_specs']['drive_type'] + \
|
||||||
|
("_%s" % volume_type['extra_specs']['drive_size'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
|
self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
|
||||||
@@ -858,9 +877,14 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
|
|
||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
"""Deletes BE volume."""
|
"""Deletes BE volume."""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).delete_volume(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:
|
try:
|
||||||
self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
|
self._sync_exec('/var/lib/zadara/bin/zadara_sncfg',
|
||||||
'delete_partition',
|
'delete_partition',
|
||||||
@@ -874,16 +898,26 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
LOG.debug(_("VSA BE delete_volume for %s suceeded"), volume['name'])
|
LOG.debug(_("VSA BE delete_volume for %s suceeded"), volume['name'])
|
||||||
|
|
||||||
def local_path(self, volume):
|
def local_path(self, volume):
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).local_path(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'])
|
||||||
|
return super(ZadaraBEDriver, self).discover_volume(None, volume)
|
||||||
|
|
||||||
raise exception.Error(_("local_path not supported"))
|
raise exception.Error(_("local_path not supported"))
|
||||||
|
|
||||||
def ensure_export(self, context, volume):
|
def ensure_export(self, context, volume):
|
||||||
"""ensure BE export for a volume"""
|
"""ensure BE export for a volume"""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).ensure_export(context, 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:
|
try:
|
||||||
iscsi_target = self.db.volume_get_iscsi_target_num(context,
|
iscsi_target = self.db.volume_get_iscsi_target_num(context,
|
||||||
volume['id'])
|
volume['id'])
|
||||||
@@ -900,9 +934,14 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
|
|
||||||
def create_export(self, context, volume):
|
def create_export(self, context, volume):
|
||||||
"""create BE export for a volume"""
|
"""create BE export for a volume"""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).create_export(context, 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'])
|
self._ensure_iscsi_targets(context, volume['host'])
|
||||||
iscsi_target = self.db.volume_allocate_iscsi_target(context,
|
iscsi_target = self.db.volume_allocate_iscsi_target(context,
|
||||||
volume['id'],
|
volume['id'],
|
||||||
@@ -915,9 +954,14 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
|
|
||||||
def remove_export(self, context, volume):
|
def remove_export(self, context, volume):
|
||||||
"""Removes BE export for a volume."""
|
"""Removes BE export for a volume."""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).remove_export(context, 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:
|
try:
|
||||||
iscsi_target = self.db.volume_get_iscsi_target_num(context,
|
iscsi_target = self.db.volume_get_iscsi_target_num(context,
|
||||||
volume['id'])
|
volume['id'])
|
||||||
@@ -939,14 +983,14 @@ class ZadaraBEDriver(ISCSIDriver):
|
|||||||
|
|
||||||
def create_snapshot(self, snapshot):
|
def create_snapshot(self, snapshot):
|
||||||
"""Nothing required for snapshot"""
|
"""Nothing required for snapshot"""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).create_snapshot(volume)
|
return super(ZadaraBEDriver, self).create_snapshot(volume)
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete_snapshot(self, snapshot):
|
def delete_snapshot(self, snapshot):
|
||||||
"""Nothing required to delete a snapshot"""
|
"""Nothing required to delete a snapshot"""
|
||||||
if self._not_vsa_be_volume(volume):
|
if self._not_vsa_volume_or_drive(volume):
|
||||||
return super(ZadaraBEDriver, self).delete_snapshot(volume)
|
return super(ZadaraBEDriver, self).delete_snapshot(volume)
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -45,11 +45,12 @@ intact.
|
|||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import rpc
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import manager
|
from nova import manager
|
||||||
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova.volume import volume_types
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.volume.manager')
|
LOG = logging.getLogger('nova.volume.manager')
|
||||||
@@ -144,12 +145,22 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
return volume_id
|
return volume_id
|
||||||
|
|
||||||
def _notify_vsa(self, context, volume_ref, status):
|
def _notify_vsa(self, context, volume_ref, status):
|
||||||
if volume_ref['to_vsa_id'] is not None:
|
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,
|
rpc.cast(context,
|
||||||
FLAGS.vsa_topic,
|
FLAGS.vsa_topic,
|
||||||
{"method": "vsa_volume_created",
|
{"method": "vsa_volume_created",
|
||||||
"args": {"vol_id": volume_ref['id'],
|
"args": {"vol_id": volume_ref['id'],
|
||||||
"vsa_id": volume_ref['to_vsa_id'],
|
"vsa_id": vsa_id,
|
||||||
"status": status}})
|
"status": status}})
|
||||||
|
|
||||||
def delete_volume(self, context, volume_id):
|
def delete_volume(self, context, volume_id):
|
||||||
|
|||||||
@@ -64,14 +64,12 @@ class SanISCSIDriver(ISCSIDriver):
|
|||||||
# discover_volume is still OK
|
# discover_volume is still OK
|
||||||
# undiscover_volume is still OK
|
# undiscover_volume is still OK
|
||||||
|
|
||||||
def _connect_to_ssh(self, san_ip=None):
|
def _connect_to_ssh(self):
|
||||||
if san_ip is None:
|
|
||||||
san_ip = FLAGS.san_ip
|
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
#TODO(justinsb): We need a better SSH key policy
|
#TODO(justinsb): We need a better SSH key policy
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
if FLAGS.san_password:
|
if FLAGS.san_password:
|
||||||
ssh.connect(san_ip,
|
ssh.connect(FLAGS.san_ip,
|
||||||
port=FLAGS.san_ssh_port,
|
port=FLAGS.san_ssh_port,
|
||||||
username=FLAGS.san_login,
|
username=FLAGS.san_login,
|
||||||
password=FLAGS.san_password)
|
password=FLAGS.san_password)
|
||||||
@@ -79,7 +77,7 @@ class SanISCSIDriver(ISCSIDriver):
|
|||||||
privatekeyfile = os.path.expanduser(FLAGS.san_privatekey)
|
privatekeyfile = os.path.expanduser(FLAGS.san_privatekey)
|
||||||
# It sucks that paramiko doesn't support DSA keys
|
# It sucks that paramiko doesn't support DSA keys
|
||||||
privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile)
|
privatekey = paramiko.RSAKey.from_private_key_file(privatekeyfile)
|
||||||
ssh.connect(san_ip,
|
ssh.connect(FLAGS.san_ip,
|
||||||
port=FLAGS.san_ssh_port,
|
port=FLAGS.san_ssh_port,
|
||||||
username=FLAGS.san_login,
|
username=FLAGS.san_login,
|
||||||
pkey=privatekey)
|
pkey=privatekey)
|
||||||
@@ -87,9 +85,9 @@ class SanISCSIDriver(ISCSIDriver):
|
|||||||
raise exception.Error(_("Specify san_password or san_privatekey"))
|
raise exception.Error(_("Specify san_password or san_privatekey"))
|
||||||
return ssh
|
return ssh
|
||||||
|
|
||||||
def _run_ssh(self, command, check_exit_code=True, san_ip=None):
|
def _run_ssh(self, command, check_exit_code=True):
|
||||||
#TODO(justinsb): SSH connection caching (?)
|
#TODO(justinsb): SSH connection caching (?)
|
||||||
ssh = self._connect_to_ssh(san_ip)
|
ssh = self._connect_to_ssh()
|
||||||
|
|
||||||
#TODO(justinsb): Reintroduce the retry hack
|
#TODO(justinsb): Reintroduce the retry hack
|
||||||
ret = ssh_execute(ssh, command, check_exit_code=check_exit_code)
|
ret = ssh_execute(ssh, command, check_exit_code=check_exit_code)
|
||||||
|
|||||||
@@ -100,20 +100,22 @@ def get_all_types(context, inactive=0, search_opts={}):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if filter_func(type_args, values):
|
if filter_func(type_args, values):
|
||||||
# if one of conditions didn't match - remove
|
|
||||||
result[type_name] = type_args
|
result[type_name] = type_args
|
||||||
break
|
break
|
||||||
vol_types = result
|
vol_types = result
|
||||||
return vol_types
|
return vol_types
|
||||||
|
|
||||||
|
|
||||||
def get_volume_type(context, id):
|
def get_volume_type(ctxt, id):
|
||||||
"""Retrieves single volume type by id."""
|
"""Retrieves single volume type by id."""
|
||||||
if id is None:
|
if id is None:
|
||||||
raise exception.InvalidVolumeType(volume_type=id)
|
raise exception.InvalidVolumeType(volume_type=id)
|
||||||
|
|
||||||
|
if ctxt is None:
|
||||||
|
ctxt = context.get_admin_context()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return db.volume_type_get(context, id)
|
return db.volume_type_get(ctxt, id)
|
||||||
except exception.DBError:
|
except exception.DBError:
|
||||||
raise exception.ApiError(_("Unknown volume type: %s") % id)
|
raise exception.ApiError(_("Unknown volume type: %s") % id)
|
||||||
|
|
||||||
@@ -127,3 +129,38 @@ def get_volume_type_by_name(context, name):
|
|||||||
return db.volume_type_get_by_name(context, name)
|
return db.volume_type_get_by_name(context, name)
|
||||||
except exception.DBError:
|
except exception.DBError:
|
||||||
raise exception.ApiError(_("Unknown volume type: %s") % name)
|
raise exception.ApiError(_("Unknown volume type: %s") % name)
|
||||||
|
|
||||||
|
|
||||||
|
def is_key_value_present(volume_type_id, key, value, volume_type=None):
|
||||||
|
if volume_type_id is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if volume_type is None:
|
||||||
|
volume_type = get_volume_type(context.get_admin_context(),
|
||||||
|
volume_type_id)
|
||||||
|
if volume_type.get('extra_specs') is None or\
|
||||||
|
volume_type['extra_specs'].get(key) != value:
|
||||||
|
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)
|
||||||
|
|||||||
148
nova/vsa/api.py
148
nova/vsa/api.py
@@ -20,22 +20,26 @@ Handles all requests relating to Virtual Storage Arrays (VSAs).
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import base64
|
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
|
||||||
|
|
||||||
|
from nova import compute
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import quota
|
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova.db import base
|
|
||||||
|
|
||||||
from nova import compute
|
|
||||||
from nova import volume
|
from nova import volume
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
from nova.vsa import drive_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
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@@ -43,22 +47,14 @@ flags.DEFINE_string('vsa_ec2_access_key', None,
|
|||||||
'EC2 access key used by VSA for accessing nova')
|
'EC2 access key used by VSA for accessing nova')
|
||||||
flags.DEFINE_string('vsa_ec2_user_id', None,
|
flags.DEFINE_string('vsa_ec2_user_id', None,
|
||||||
'User ID used by VSA for accessing nova')
|
'User ID used by VSA for accessing nova')
|
||||||
|
|
||||||
flags.DEFINE_boolean('vsa_multi_vol_creation', True,
|
flags.DEFINE_boolean('vsa_multi_vol_creation', True,
|
||||||
'Ask scheduler to create multiple volumes in one call')
|
'Ask scheduler to create multiple volumes in one call')
|
||||||
|
flags.DEFINE_string('vsa_volume_type_name', 'VSA volume type',
|
||||||
|
'Name of volume type associated with FE VSA volumes')
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.vsa')
|
LOG = logging.getLogger('nova.vsa')
|
||||||
|
|
||||||
|
|
||||||
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 storage allocations failed
|
|
||||||
FAILED = 'failed' # Some BE storage allocations failed
|
|
||||||
DELETING = 'deleting' # VSA started the deletion procedure
|
|
||||||
|
|
||||||
|
|
||||||
class API(base.Base):
|
class API(base.Base):
|
||||||
"""API for interacting with the VSA manager."""
|
"""API for interacting with the VSA manager."""
|
||||||
|
|
||||||
@@ -67,6 +63,15 @@ class API(base.Base):
|
|||||||
self.volume_api = volume_api or volume.API()
|
self.volume_api = volume_api or volume.API()
|
||||||
super(API, self).__init__(**kwargs)
|
super(API, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def _check_volume_type_correctness(self, vol_type):
|
||||||
|
if vol_type.get('extra_specs') == None or\
|
||||||
|
vol_type['extra_specs'].get('type') != 'vsa_drive' or\
|
||||||
|
vol_type['extra_specs'].get('drive_type') == None or\
|
||||||
|
vol_type['extra_specs'].get('drive_size') == None:
|
||||||
|
|
||||||
|
raise exception.ApiError(_("Invalid drive type %s")
|
||||||
|
% vol_type['name'])
|
||||||
|
|
||||||
def _get_default_vsa_instance_type(self):
|
def _get_default_vsa_instance_type(self):
|
||||||
return instance_types.get_instance_type_by_name(
|
return instance_types.get_instance_type_by_name(
|
||||||
FLAGS.default_vsa_instance_type)
|
FLAGS.default_vsa_instance_type)
|
||||||
@@ -89,16 +94,17 @@ class API(base.Base):
|
|||||||
if name is None:
|
if name is None:
|
||||||
raise exception.ApiError(_("No drive_name param found in %s")
|
raise exception.ApiError(_("No drive_name param found in %s")
|
||||||
% node)
|
% node)
|
||||||
|
|
||||||
# find DB record for this disk
|
|
||||||
try:
|
try:
|
||||||
drive_ref = drive_types.get_by_name(context, name)
|
vol_type = volume_types.get_volume_type_by_name(context, name)
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
raise exception.ApiError(_("Invalid drive type name %s")
|
raise exception.ApiError(_("Invalid drive type name %s")
|
||||||
% name)
|
% name)
|
||||||
|
|
||||||
|
self._check_volume_type_correctness(vol_type)
|
||||||
|
|
||||||
# if size field present - override disk size specified in DB
|
# if size field present - override disk size specified in DB
|
||||||
size = node.get('size', drive_ref['size_gb'])
|
size = int(node.get('size',
|
||||||
|
vol_type['extra_specs'].get('drive_size')))
|
||||||
|
|
||||||
if shared:
|
if shared:
|
||||||
part_size = FLAGS.vsa_part_size_gb
|
part_size = FLAGS.vsa_part_size_gb
|
||||||
@@ -110,17 +116,15 @@ class API(base.Base):
|
|||||||
size = 0 # special handling for full drives
|
size = 0 # special handling for full drives
|
||||||
|
|
||||||
for i in range(num_volumes):
|
for i in range(num_volumes):
|
||||||
# volume_name = vsa_name + ("_%s_vol-%d" % (name, i))
|
|
||||||
volume_name = "drive-%03d" % first_index
|
volume_name = "drive-%03d" % first_index
|
||||||
first_index += 1
|
first_index += 1
|
||||||
volume_desc = 'BE volume for VSA %s type %s' % \
|
volume_desc = 'BE volume for VSA %s type %s' % \
|
||||||
(vsa_name, name)
|
(vsa_name, name)
|
||||||
volume = {
|
volume = {
|
||||||
'size': size,
|
'size': size,
|
||||||
'snapshot_id': None,
|
|
||||||
'name': volume_name,
|
'name': volume_name,
|
||||||
'description': volume_desc,
|
'description': volume_desc,
|
||||||
'drive_ref': drive_ref
|
'volume_type_id': vol_type['id'],
|
||||||
}
|
}
|
||||||
volume_params.append(volume)
|
volume_params.append(volume)
|
||||||
|
|
||||||
@@ -211,7 +215,7 @@ class API(base.Base):
|
|||||||
if len(volume_params) > 0:
|
if len(volume_params) > 0:
|
||||||
request_spec = {
|
request_spec = {
|
||||||
'num_volumes': len(volume_params),
|
'num_volumes': len(volume_params),
|
||||||
'vsa_id': vsa_id,
|
'vsa_id': str(vsa_id),
|
||||||
'volumes': volume_params,
|
'volumes': volume_params,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,17 +231,21 @@ class API(base.Base):
|
|||||||
try:
|
try:
|
||||||
vol_name = vol['name']
|
vol_name = vol['name']
|
||||||
vol_size = vol['size']
|
vol_size = vol['size']
|
||||||
|
vol_type_id = vol['volume_type_id']
|
||||||
LOG.debug(_("VSA ID %(vsa_id)d %(vsa_name)s: Create "\
|
LOG.debug(_("VSA ID %(vsa_id)d %(vsa_name)s: Create "\
|
||||||
"volume %(vol_name)s, %(vol_size)d GB"),
|
"volume %(vol_name)s, %(vol_size)d GB, "\
|
||||||
locals())
|
"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_ref = self.volume_api.create(context,
|
||||||
vol_size,
|
vol_size,
|
||||||
vol['snapshot_id'],
|
None,
|
||||||
vol_name,
|
vol_name,
|
||||||
vol['description'],
|
vol['description'],
|
||||||
to_vsa_id=vsa_id,
|
volume_type=vol_type,
|
||||||
drive_type_id=vol['drive_ref'].get('id'),
|
metadata=dict(to_vsa_id=str(vsa_id)),
|
||||||
availability_zone=availability_zone)
|
availability_zone=availability_zone)
|
||||||
except:
|
except:
|
||||||
self.update_vsa_status(context, vsa_id,
|
self.update_vsa_status(context, vsa_id,
|
||||||
@@ -249,7 +257,7 @@ class API(base.Base):
|
|||||||
rpc.cast(context,
|
rpc.cast(context,
|
||||||
FLAGS.vsa_topic,
|
FLAGS.vsa_topic,
|
||||||
{"method": "create_vsa",
|
{"method": "create_vsa",
|
||||||
"args": {"vsa_id": vsa_id}})
|
"args": {"vsa_id": str(vsa_id)}})
|
||||||
|
|
||||||
return vsa_ref
|
return vsa_ref
|
||||||
|
|
||||||
@@ -314,8 +322,7 @@ class API(base.Base):
|
|||||||
def _force_volume_delete(self, ctxt, volume):
|
def _force_volume_delete(self, ctxt, volume):
|
||||||
"""Delete a volume, bypassing the check that it must be available."""
|
"""Delete a volume, bypassing the check that it must be available."""
|
||||||
host = volume['host']
|
host = volume['host']
|
||||||
if not host or volume['from_vsa_id']:
|
if not host:
|
||||||
# Volume not yet assigned to host OR FE volume
|
|
||||||
# Deleting volume from database and skipping rpc.
|
# Deleting volume from database and skipping rpc.
|
||||||
self.db.volume_destroy(ctxt, volume['id'])
|
self.db.volume_destroy(ctxt, volume['id'])
|
||||||
return
|
return
|
||||||
@@ -328,9 +335,9 @@ class API(base.Base):
|
|||||||
def delete_vsa_volumes(self, context, vsa_id, direction,
|
def delete_vsa_volumes(self, context, vsa_id, direction,
|
||||||
force_delete=True):
|
force_delete=True):
|
||||||
if direction == "FE":
|
if direction == "FE":
|
||||||
volumes = self.db.volume_get_all_assigned_from_vsa(context, vsa_id)
|
volumes = self.get_all_vsa_volumes(context, vsa_id)
|
||||||
else:
|
else:
|
||||||
volumes = self.db.volume_get_all_assigned_to_vsa(context, vsa_id)
|
volumes = self.get_all_vsa_drives(context, vsa_id)
|
||||||
|
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
try:
|
try:
|
||||||
@@ -374,58 +381,25 @@ class API(base.Base):
|
|||||||
return self.db.vsa_get_all(context)
|
return self.db.vsa_get_all(context)
|
||||||
return self.db.vsa_get_all_by_project(context, context.project_id)
|
return self.db.vsa_get_all_by_project(context, context.project_id)
|
||||||
|
|
||||||
def generate_user_data(self, context, vsa, volumes):
|
def get_vsa_volume_type(self, context):
|
||||||
SubElement = ElementTree.SubElement
|
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)
|
||||||
|
|
||||||
e_vsa = ElementTree.Element("vsa")
|
return vol_type
|
||||||
|
|
||||||
e_vsa_detail = SubElement(e_vsa, "id")
|
def get_all_vsa_instances(self, context, vsa_id):
|
||||||
e_vsa_detail.text = str(vsa['id'])
|
return self.compute_api.get_all(context,
|
||||||
e_vsa_detail = SubElement(e_vsa, "name")
|
search_opts={'metadata': dict(vsa_id=str(vsa_id))})
|
||||||
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")
|
def get_all_vsa_volumes(self, context, vsa_id):
|
||||||
e_vsa_detail.text = FLAGS.vsa_ec2_user_id
|
return self.volume_api.get_all(context,
|
||||||
e_vsa_detail = SubElement(e_vsa, "auth_access_key")
|
search_opts={'metadata': dict(from_vsa_id=str(vsa_id))})
|
||||||
e_vsa_detail.text = FLAGS.vsa_ec2_access_key
|
|
||||||
|
|
||||||
e_volumes = SubElement(e_vsa, "volumes")
|
def get_all_vsa_drives(self, context, vsa_id):
|
||||||
for volume in volumes:
|
return self.volume_api.get_all(context,
|
||||||
|
search_opts={'metadata': dict(to_vsa_id=str(vsa_id))})
|
||||||
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)
|
|
||||||
|
|||||||
@@ -1,114 +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).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from nova import db
|
|
||||||
from nova import exception
|
|
||||||
from nova import flags
|
|
||||||
from nova import log as logging
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
flags.DEFINE_string('drive_type_template_short', '%s_%sGB_%sRPM',
|
|
||||||
'Template string for generation of drive type name')
|
|
||||||
flags.DEFINE_string('drive_type_template_long', '%s_%sGB_%sRPM_%s',
|
|
||||||
'Template string for generation of drive type name')
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger('nova.drive_types')
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_default_drive_name(type, size_gb, rpm, capabilities):
|
|
||||||
if capabilities is None or capabilities == '':
|
|
||||||
return FLAGS.drive_type_template_short % \
|
|
||||||
(type, str(size_gb), rpm)
|
|
||||||
else:
|
|
||||||
return FLAGS.drive_type_template_long % \
|
|
||||||
(type, str(size_gb), rpm, capabilities)
|
|
||||||
|
|
||||||
|
|
||||||
def create(context, type, size_gb, rpm, capabilities='',
|
|
||||||
visible=True, name=None):
|
|
||||||
if name is None:
|
|
||||||
name = _generate_default_drive_name(type, size_gb, rpm,
|
|
||||||
capabilities)
|
|
||||||
LOG.debug(_("Creating drive type %(name)s: "\
|
|
||||||
"%(type)s %(size_gb)s %(rpm)s %(capabilities)s"), locals())
|
|
||||||
|
|
||||||
values = {
|
|
||||||
'type': type,
|
|
||||||
'size_gb': size_gb,
|
|
||||||
'rpm': rpm,
|
|
||||||
'capabilities': capabilities,
|
|
||||||
'visible': visible,
|
|
||||||
'name': name
|
|
||||||
}
|
|
||||||
return db.drive_type_create(context, values)
|
|
||||||
|
|
||||||
|
|
||||||
def update(context, id, **kwargs):
|
|
||||||
|
|
||||||
LOG.debug(_("Updating drive type with id %(id)s: %(kwargs)s"), locals())
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
# call update regadless if changes is empty or not
|
|
||||||
return db.drive_type_update(context, id, changes)
|
|
||||||
|
|
||||||
|
|
||||||
def rename(context, name, new_name=None):
|
|
||||||
|
|
||||||
if new_name is None or \
|
|
||||||
new_name == '':
|
|
||||||
disk = db.drive_type_get_by_name(context, name)
|
|
||||||
new_name = _generate_default_drive_name(disk['type'],
|
|
||||||
disk['size_gb'], disk['rpm'], disk['capabilities'])
|
|
||||||
|
|
||||||
LOG.debug(_("Renaming drive type %(name)s to %(new_name)s"), locals())
|
|
||||||
|
|
||||||
values = dict(name=new_name)
|
|
||||||
dtype = db.drive_type_get_by_name(context, name)
|
|
||||||
return db.drive_type_update(context, dtype['id'], values)
|
|
||||||
|
|
||||||
|
|
||||||
def delete(context, id):
|
|
||||||
LOG.debug(_("Deleting drive type %d"), id)
|
|
||||||
db.drive_type_destroy(context, id)
|
|
||||||
|
|
||||||
|
|
||||||
def get(context, id):
|
|
||||||
return db.drive_type_get(context, id)
|
|
||||||
|
|
||||||
|
|
||||||
def get_by_name(context, name):
|
|
||||||
return db.drive_type_get_by_name(context, name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_all(context, visible=True):
|
|
||||||
return db.drive_type_get_all(context, visible)
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
class FakeVcConnection:
|
class FakeVcConnection(object):
|
||||||
|
|
||||||
def init_host(self, host):
|
def init_host(self, host):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -22,17 +22,17 @@ Handles all processes relating to Virtual Storage Arrays (VSA).
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from nova import compute
|
||||||
|
from nova import exception
|
||||||
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import manager
|
from nova import manager
|
||||||
from nova import flags
|
|
||||||
from nova import utils
|
|
||||||
from nova import exception
|
|
||||||
from nova import compute
|
|
||||||
from nova import volume
|
from nova import volume
|
||||||
from nova import vsa
|
from nova import vsa
|
||||||
from nova.vsa.api import VsaState
|
from nova import utils
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
|
from nova.vsa import utils as vsa_utils
|
||||||
|
from nova.vsa.api import VsaState
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_string('vsa_driver', 'nova.vsa.connection.get_connection',
|
flags.DEFINE_string('vsa_driver', 'nova.vsa.connection.get_connection',
|
||||||
@@ -83,18 +83,18 @@ class VsaManager(manager.SchedulerDependentManager):
|
|||||||
@exception.wrap_exception()
|
@exception.wrap_exception()
|
||||||
def vsa_volume_created(self, context, vol_id, vsa_id, status):
|
def vsa_volume_created(self, context, vol_id, vsa_id, status):
|
||||||
"""Callback for volume creations"""
|
"""Callback for volume creations"""
|
||||||
LOG.debug(_("VSA ID %(vsa_id)s: Volume %(vol_id)s created. "\
|
LOG.debug(_("VSA ID %(vsa_id)s: Drive %(vol_id)s created. "\
|
||||||
"Status %(status)s"), locals())
|
"Status %(status)s"), locals())
|
||||||
vsa_id = int(vsa_id) # just in case
|
vsa_id = int(vsa_id) # just in case
|
||||||
|
|
||||||
# Get all volumes for this VSA
|
# Get all volumes for this VSA
|
||||||
# check if any of them still in creating phase
|
# check if any of them still in creating phase
|
||||||
volumes = self.db.volume_get_all_assigned_to_vsa(context, vsa_id)
|
drives = self.vsa_api.get_all_vsa_drives(context, vsa_id)
|
||||||
for volume in volumes:
|
for drive in drives:
|
||||||
if volume['status'] == 'creating':
|
if drive['status'] == 'creating':
|
||||||
vol_name = volume['name']
|
vol_name = drive['name']
|
||||||
vol_disp_name = volume['display_name']
|
vol_disp_name = drive['display_name']
|
||||||
LOG.debug(_("Volume %(vol_name)s (%(vol_disp_name)s) still "\
|
LOG.debug(_("Drive %(vol_name)s (%(vol_disp_name)s) still "\
|
||||||
"in creating phase - wait"), locals())
|
"in creating phase - wait"), locals())
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -105,17 +105,17 @@ class VsaManager(manager.SchedulerDependentManager):
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(volumes) != vsa['vol_count']:
|
if len(drives) != vsa['vol_count']:
|
||||||
cvol_real = len(volumes)
|
cvol_real = len(drives)
|
||||||
cvol_exp = vsa['vol_count']
|
cvol_exp = vsa['vol_count']
|
||||||
LOG.debug(_("VSA ID %(vsa_id)d: Not all volumes are created "\
|
LOG.debug(_("VSA ID %(vsa_id)d: Not all volumes are created "\
|
||||||
"(%(cvol_real)d of %(cvol_exp)d)"), locals())
|
"(%(cvol_real)d of %(cvol_exp)d)"), locals())
|
||||||
return
|
return
|
||||||
|
|
||||||
# all volumes created (successfully or not)
|
# all volumes created (successfully or not)
|
||||||
return self._start_vcs(context, vsa, volumes)
|
return self._start_vcs(context, vsa, drives)
|
||||||
|
|
||||||
def _start_vcs(self, context, vsa, volumes=[]):
|
def _start_vcs(self, context, vsa, drives=[]):
|
||||||
"""Start VCs for VSA """
|
"""Start VCs for VSA """
|
||||||
|
|
||||||
vsa_id = vsa['id']
|
vsa_id = vsa['id']
|
||||||
@@ -127,11 +127,11 @@ class VsaManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
# in _separate_ loop go over all volumes and mark as "attached"
|
# in _separate_ loop go over all volumes and mark as "attached"
|
||||||
has_failed_volumes = False
|
has_failed_volumes = False
|
||||||
for volume in volumes:
|
for drive in drives:
|
||||||
vol_name = volume['name']
|
vol_name = drive['name']
|
||||||
vol_disp_name = volume['display_name']
|
vol_disp_name = drive['display_name']
|
||||||
status = volume['status']
|
status = drive['status']
|
||||||
LOG.info(_("VSA ID %(vsa_id)d: Volume %(vol_name)s "\
|
LOG.info(_("VSA ID %(vsa_id)d: Drive %(vol_name)s "\
|
||||||
"(%(vol_disp_name)s) is in %(status)s state"),
|
"(%(vol_disp_name)s) is in %(status)s state"),
|
||||||
locals())
|
locals())
|
||||||
if status == 'available':
|
if status == 'available':
|
||||||
@@ -149,11 +149,12 @@ class VsaManager(manager.SchedulerDependentManager):
|
|||||||
if has_failed_volumes:
|
if has_failed_volumes:
|
||||||
LOG.info(_("VSA ID %(vsa_id)d: Delete all BE volumes"), locals())
|
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.delete_vsa_volumes(context, vsa_id, "BE", True)
|
||||||
self.vsa_api.update_vsa_status(context, vsa_id, VsaState.FAILED)
|
self.vsa_api.update_vsa_status(context, vsa_id,
|
||||||
|
VsaState.FAILED)
|
||||||
return
|
return
|
||||||
|
|
||||||
# create user-data record for VC
|
# create user-data record for VC
|
||||||
storage_data = self.vsa_api.generate_user_data(context, vsa, volumes)
|
storage_data = vsa_utils.generate_user_data(vsa, drives)
|
||||||
|
|
||||||
instance_type = instance_types.get_instance_type(
|
instance_type = instance_types.get_instance_type(
|
||||||
vsa['instance_type_id'])
|
vsa['instance_type_id'])
|
||||||
@@ -174,4 +175,5 @@ class VsaManager(manager.SchedulerDependentManager):
|
|||||||
user_data=storage_data,
|
user_data=storage_data,
|
||||||
metadata=dict(vsa_id=str(vsa_id)))
|
metadata=dict(vsa_id=str(vsa_id)))
|
||||||
|
|
||||||
self.vsa_api.update_vsa_status(context, vsa_id, VsaState.CREATED)
|
self.vsa_api.update_vsa_status(context, vsa_id,
|
||||||
|
VsaState.CREATED)
|
||||||
|
|||||||
80
nova/vsa/utils.py
Normal file
80
nova/vsa/utils.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 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)
|
||||||
Reference in New Issue
Block a user