Files
nova/nova/volume/xensm.py
T
Vishvananda Ishaya 20b4d89512 Remove a whole bunch of unused imports
Change-Id: I6759e5b6250c48cc0deb4b198b44c948c64c47d1
2012-01-13 13:55:38 -08:00

241 lines
9.0 KiB
Python

# Copyright (c) 2011 Citrix Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
from nova.volume.driver import VolumeDriver
from nova.virt.xenapi_conn import XenAPISession
from nova.virt.xenapi.volumeops import VolumeOps
LOG = logging.getLogger("nova.volume.xensm")
FLAGS = flags.FLAGS
class XenSMDriver(VolumeDriver):
def _convert_config_params(self, conf_str):
params = dict([item.split("=") for item in conf_str.split()])
return params
def _get_introduce_sr_keys(self, params):
if 'name_label' in params:
del params['name_label']
keys = params.keys()
keys.append('sr_type')
return keys
def _create_storage_repo(self, context, backend_ref):
"""Either creates or introduces SR on host
depending on whether it exists in xapi db."""
params = self._convert_config_params(backend_ref['config_params'])
if 'name_label' in params:
label = params['name_label']
del params['name_label']
else:
label = 'SR-' + str(backend_ref['id'])
params['sr_type'] = backend_ref['sr_type']
if backend_ref['sr_uuid'] is None:
# run the sr create command
try:
LOG.debug(_('SR name = %s') % label)
LOG.debug(_('Params: %s') % str(params))
sr_uuid = self._volumeops.create_sr(label, params)
# update sr_uuid and created in db
except Exception as ex:
LOG.debug(_("Failed to create sr %s...continuing") \
% str(backend_ref['id']))
raise exception.Error(_('Create failed'))
LOG.debug(_('SR UUID of new SR is: %s') % sr_uuid)
try:
self.db.sm_backend_conf_update(context,
backend_ref['id'],
dict(sr_uuid=sr_uuid))
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to update db"))
else:
# sr introduce, if not already done
try:
self._volumeops.introduce_sr(backend_ref['sr_uuid'], label,
params)
except Exception as ex:
LOG.exception(ex)
LOG.debug(_("Failed to introduce sr %s...continuing") \
% str(backend_ref['id']))
def _create_storage_repos(self, context):
"""Create/Introduce storage repositories at start."""
backends = self.db.sm_backend_conf_get_all(context)
for backend in backends:
try:
self._create_storage_repo(context, backend)
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_('Failed to reach backend %d') \
% backend['id'])
def __init__(self, *args, **kwargs):
"""Connect to the hypervisor."""
# This driver leverages Xen storage manager, and hence requires
# hypervisor to be Xen
if FLAGS.connection_type != 'xenapi':
raise exception.Error(_('XenSMDriver requires xenapi connection'))
url = FLAGS.xenapi_connection_url
username = FLAGS.xenapi_connection_username
password = FLAGS.xenapi_connection_password
try:
session = XenAPISession(url, username, password)
self._volumeops = VolumeOps(session)
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to initiate session"))
super(XenSMDriver, self).__init__(execute=utils.execute,
sync_exec=utils.execute,
*args, **kwargs)
def do_setup(self, ctxt):
"""Setup includes creating or introducing storage repos
existing in the database and destroying deleted ones."""
# TODO purge storage repos
self.ctxt = ctxt
self._create_storage_repos(ctxt)
def create_volume(self, volume):
"""Creates a logical volume. Can optionally return a Dictionary of
changes to the volume object to be persisted."""
# For now the scheduling logic will be to try to fit the volume in
# the first available backend.
# TODO better scheduling once APIs are in place
sm_vol_rec = None
backends = self.db.sm_backend_conf_get_all(self.ctxt)
for backend in backends:
# Ensure that storage repo exists, if not create.
# This needs to be done because if nova compute and
# volume are both running on this host, then, as a
# part of detach_volume, compute could potentially forget SR
self._create_storage_repo(self.ctxt, backend)
sm_vol_rec = self._volumeops.\
create_volume_for_sm(volume,
backend['sr_uuid'])
if sm_vol_rec:
LOG.debug(_('Volume will be created in backend - %d') \
% backend['id'])
break
if sm_vol_rec:
# Update db
sm_vol_rec['id'] = volume['id']
sm_vol_rec['backend_id'] = backend['id']
try:
self.db.sm_volume_create(self.ctxt, sm_vol_rec)
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to update volume in db"))
else:
raise exception.Error(_('Unable to create volume'))
def delete_volume(self, volume):
vol_rec = self.db.sm_volume_get(self.ctxt, volume['id'])
try:
# If compute runs on this node, detach could have disconnected SR
backend_ref = self.db.sm_backend_conf_get(self.ctxt,
vol_rec['backend_id'])
self._create_storage_repo(self.ctxt, backend_ref)
self._volumeops.delete_volume_for_sm(vol_rec['vdi_uuid'])
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to delete vdi"))
try:
self.db.sm_volume_delete(self.ctxt, volume['id'])
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to delete volume in db"))
def local_path(self, volume):
return str(volume['id'])
def undiscover_volume(self, volume):
"""Undiscover volume on a remote host."""
pass
def discover_volume(self, context, volume):
return str(volume['id'])
def check_for_setup_error(self):
pass
def create_export(self, context, volume):
"""Exports the volume."""
# !!! TODO
pass
def remove_export(self, context, volume):
"""Removes an export for a logical volume."""
pass
def ensure_export(self, context, volume):
"""Safely, synchronously recreates an export for a logical volume."""
pass
def initialize_connection(self, volume, address):
try:
xensm_properties = dict(self.db.sm_volume_get(self.ctxt,
volume['id']))
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to find volume in db"))
# Keep the volume id key consistent with what ISCSI driver calls it
xensm_properties['volume_id'] = xensm_properties['id']
del xensm_properties['id']
try:
backend_conf = self.db.\
sm_backend_conf_get(self.ctxt,
xensm_properties['backend_id'])
except Exception as ex:
LOG.exception(ex)
raise exception.Error(_("Failed to find backend in db"))
params = self._convert_config_params(backend_conf['config_params'])
xensm_properties['flavor_id'] = backend_conf['flavor_id']
xensm_properties['sr_uuid'] = backend_conf['sr_uuid']
xensm_properties['sr_type'] = backend_conf['sr_type']
xensm_properties.update(params)
xensm_properties['introduce_sr_keys'] = self.\
_get_introduce_sr_keys(params)
return {
'driver_volume_type': 'xensm',
'data': xensm_properties
}
def terminate_connection(self, volume, address):
pass