Remove XenAPI driver

The XenAPI driver does not have known users and does not meet
the minimum requirements for Cinder drivers.

This patch removes the driver from the tree.

Closes-bug: 1372836

Change-Id: I2123b2d52180bbdffe90c458f115c67f50dfd5e9
This commit is contained in:
Bob Ball 2014-09-22 16:29:00 +01:00
parent db2c817fc7
commit cd0def136c
6 changed files with 0 additions and 1349 deletions

View File

@ -1,507 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import mock
import mox
import six
from cinder.db import api as db_api
from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.xenapi import lib
from cinder.volume.drivers.xenapi import sm as driver
from cinder.volume.drivers.xenapi import tools
class MockContext(object):
def __init__(ctxt, auth_token):
ctxt.auth_token = auth_token
@contextlib.contextmanager
def simple_context(value):
yield value
def get_configured_driver(server='ignore_server', path='ignore_path'):
configuration = mox.MockObject(conf.Configuration)
configuration.xenapi_nfs_server = server
configuration.xenapi_nfs_serverpath = path
configuration.append_config_values(mox.IgnoreArg())
configuration.volume_dd_blocksize = '1M'
return driver.XenAPINFSDriver(configuration=configuration)
class DriverTestCase(test.TestCase):
def assert_flag(self, flagname):
self.assertTrue(hasattr(driver.CONF, flagname))
def test_config_options(self):
self.assert_flag('xenapi_connection_url')
self.assert_flag('xenapi_connection_username')
self.assert_flag('xenapi_connection_password')
self.assert_flag('xenapi_nfs_server')
self.assert_flag('xenapi_nfs_serverpath')
self.assert_flag('xenapi_sr_base_path')
def test_do_setup(self):
mock = mox.Mox()
mock.StubOutWithMock(driver, 'xenapi_lib')
mock.StubOutWithMock(driver, 'xenapi_opts')
configuration = mox.MockObject(conf.Configuration)
configuration.xenapi_connection_url = 'url'
configuration.xenapi_connection_username = 'user'
configuration.xenapi_connection_password = 'pass'
configuration.append_config_values(mox.IgnoreArg())
session_factory = object()
nfsops = object()
driver.xenapi_lib.SessionFactory('url', 'user', 'pass').AndReturn(
session_factory)
driver.xenapi_lib.NFSBasedVolumeOperations(
session_factory).AndReturn(nfsops)
drv = driver.XenAPINFSDriver(configuration=configuration)
mock.ReplayAll()
drv.do_setup('context')
mock.VerifyAll()
self.assertEqual(nfsops, drv.nfs_ops)
def test_create_volume(self):
mock = mox.Mox()
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
drv = get_configured_driver('server', 'path')
drv.nfs_ops = ops
volume_details = dict(
sr_uuid='sr_uuid',
vdi_uuid='vdi_uuid'
)
ops.create_volume(
'server', 'path', 1, 'name', 'desc').AndReturn(volume_details)
mock.ReplayAll()
result = drv.create_volume(dict(
size=1, display_name='name', display_description='desc'))
mock.VerifyAll()
self.assertEqual(dict(provider_location='sr_uuid/vdi_uuid'), result)
def test_delete_volume(self):
mock = mox.Mox()
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
drv = get_configured_driver('server', 'path')
drv.nfs_ops = ops
ops.delete_volume('server', 'path', 'sr_uuid', 'vdi_uuid')
mock.ReplayAll()
drv.delete_volume(dict(provider_location='sr_uuid/vdi_uuid'))
mock.VerifyAll()
def test_create_export_does_not_raise_exception(self):
configuration = conf.Configuration([])
drv = driver.XenAPINFSDriver(configuration=configuration)
drv.create_export('context', 'volume')
def test_remove_export_does_not_raise_exception(self):
configuration = conf.Configuration([])
drv = driver.XenAPINFSDriver(configuration=configuration)
drv.remove_export('context', 'volume')
def test_initialize_connection(self):
mock = mox.Mox()
drv = get_configured_driver('server', 'path')
mock.ReplayAll()
result = drv.initialize_connection(
dict(
display_name='name',
display_description='desc',
provider_location='sr_uuid/vdi_uuid'),
'connector'
)
mock.VerifyAll()
self.assertEqual(
dict(
driver_volume_type='xensm',
data=dict(
name_label='name',
name_description='desc',
sr_uuid='sr_uuid',
vdi_uuid='vdi_uuid',
sr_type='nfs',
server='server',
serverpath='path',
introduce_sr_keys=['sr_type', 'server', 'serverpath']
)
),
result
)
def test_initialize_connection_null_values(self):
mock = mox.Mox()
drv = get_configured_driver('server', 'path')
mock.ReplayAll()
result = drv.initialize_connection(
dict(
display_name=None,
display_description=None,
provider_location='sr_uuid/vdi_uuid'),
'connector'
)
mock.VerifyAll()
self.assertEqual(
dict(
driver_volume_type='xensm',
data=dict(
name_label='',
name_description='',
sr_uuid='sr_uuid',
vdi_uuid='vdi_uuid',
sr_type='nfs',
server='server',
serverpath='path',
introduce_sr_keys=['sr_type', 'server', 'serverpath']
)
),
result
)
def _setup_mock_driver(self, server, serverpath, sr_base_path="_srbp"):
mock = mox.Mox()
drv = get_configured_driver(server, serverpath)
ops = mock.CreateMock(lib.NFSBasedVolumeOperations)
db = mock.CreateMock(db_api)
drv.nfs_ops = ops
drv.db = db
mock.StubOutWithMock(driver, 'CONF')
driver.CONF.xenapi_nfs_server = server
driver.CONF.xenapi_nfs_serverpath = serverpath
driver.CONF.xenapi_sr_base_path = sr_base_path
return mock, drv
def test_create_snapshot(self):
mock, drv = self._setup_mock_driver('server', 'serverpath')
snapshot = dict(
volume_id="volume-id",
display_name="snapshot-name",
display_description="snapshot-desc",
volume=dict(provider_location="sr-uuid/vdi-uuid"))
drv.nfs_ops.copy_volume(
"server", "serverpath", "sr-uuid", "vdi-uuid",
"snapshot-name", "snapshot-desc"
).AndReturn(dict(sr_uuid="copied-sr", vdi_uuid="copied-vdi"))
mock.ReplayAll()
result = drv.create_snapshot(snapshot)
mock.VerifyAll()
self.assertEqual(
dict(provider_location="copied-sr/copied-vdi"),
result)
def test_create_volume_from_snapshot(self):
mock, drv = self._setup_mock_driver('server', 'serverpath')
snapshot = dict(
provider_location='src-sr-uuid/src-vdi-uuid')
volume = dict(
display_name='tgt-name', name_description='tgt-desc')
drv.nfs_ops.copy_volume(
"server", "serverpath", "src-sr-uuid", "src-vdi-uuid",
"tgt-name", "tgt-desc"
).AndReturn(dict(sr_uuid="copied-sr", vdi_uuid="copied-vdi"))
mock.ReplayAll()
result = drv.create_volume_from_snapshot(volume, snapshot)
mock.VerifyAll()
self.assertEqual(
dict(provider_location='copied-sr/copied-vdi'), result)
def test_delete_snapshot(self):
mock, drv = self._setup_mock_driver('server', 'serverpath')
snapshot = dict(
provider_location='src-sr-uuid/src-vdi-uuid')
drv.nfs_ops.delete_volume(
"server", "serverpath", "src-sr-uuid", "src-vdi-uuid")
mock.ReplayAll()
drv.delete_snapshot(snapshot)
mock.VerifyAll()
def test_copy_volume_to_image_xenserver_case(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
mock.StubOutWithMock(drv, '_use_glance_plugin_to_upload_volume')
mock.StubOutWithMock(driver.image_utils, 'is_xenserver_format')
context = MockContext('token')
driver.image_utils.is_xenserver_format('image_meta').AndReturn(True)
drv._use_glance_plugin_to_upload_volume(
context, 'volume', 'image_service', 'image_meta').AndReturn(
'result')
mock.ReplayAll()
result = drv.copy_volume_to_image(
context, "volume", "image_service", "image_meta")
self.assertEqual('result', result)
mock.VerifyAll()
def test_copy_volume_to_image_non_xenserver_case(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
mock.StubOutWithMock(drv, '_use_image_utils_to_upload_volume')
mock.StubOutWithMock(driver.image_utils, 'is_xenserver_format')
context = MockContext('token')
driver.image_utils.is_xenserver_format('image_meta').AndReturn(False)
drv._use_image_utils_to_upload_volume(
context, 'volume', 'image_service', 'image_meta').AndReturn(
'result')
mock.ReplayAll()
result = drv.copy_volume_to_image(
context, "volume", "image_service", "image_meta")
self.assertEqual('result', result)
mock.VerifyAll()
def test_use_image_utils_to_upload_volume(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
volume = dict(provider_location='sr-uuid/vdi-uuid')
context = MockContext('token')
mock.StubOutWithMock(driver.image_utils, 'upload_volume')
drv.nfs_ops.volume_attached_here(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', True).AndReturn(
simple_context('device'))
driver.image_utils.upload_volume(
context, 'image_service', 'image_meta', 'device')
mock.ReplayAll()
drv._use_image_utils_to_upload_volume(
context, volume, "image_service", "image_meta")
mock.VerifyAll()
def test_use_glance_plugin_to_upload_volume(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
volume = dict(provider_location='sr-uuid/vdi-uuid')
context = MockContext('token')
mock.StubOutWithMock(driver.glance, 'get_api_servers')
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
drv.nfs_ops.use_glance_plugin_to_upload_volume(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
'image-id', 'token', '/var/run/sr-mount')
mock.ReplayAll()
drv._use_glance_plugin_to_upload_volume(
context, volume, "image_service", {"id": "image-id"})
mock.VerifyAll()
def test_copy_image_to_volume_xenserver_case(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
mock.StubOutWithMock(drv, '_use_glance_plugin_to_copy_image_to_volume')
mock.StubOutWithMock(driver.image_utils, 'is_xenserver_image')
context = MockContext('token')
driver.image_utils.is_xenserver_image(
context, 'image_service', 'image_id').AndReturn(True)
drv._use_glance_plugin_to_copy_image_to_volume(
context, 'volume', 'image_service', 'image_id').AndReturn('result')
mock.ReplayAll()
result = drv.copy_image_to_volume(
context, "volume", "image_service", "image_id")
self.assertEqual('result', result)
mock.VerifyAll()
def test_copy_image_to_volume_non_xenserver_case(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
mock.StubOutWithMock(drv, '_use_image_utils_to_pipe_bytes_to_volume')
mock.StubOutWithMock(driver.image_utils, 'is_xenserver_image')
context = MockContext('token')
driver.image_utils.is_xenserver_image(
context, 'image_service', 'image_id').AndReturn(False)
drv._use_image_utils_to_pipe_bytes_to_volume(
context, 'volume', 'image_service', 'image_id').AndReturn(True)
mock.ReplayAll()
drv.copy_image_to_volume(
context, "volume", "image_service", "image_id")
mock.VerifyAll()
def test_use_image_utils_to_pipe_bytes_to_volume(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
volume = dict(provider_location='sr-uuid/vdi-uuid', size=1)
context = MockContext('token')
mock.StubOutWithMock(driver.image_utils, 'fetch_to_raw')
drv.nfs_ops.volume_attached_here(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', False).AndReturn(
simple_context('device'))
driver.image_utils.fetch_to_raw(
context, 'image_service', 'image_id', 'device', mox.IgnoreArg(),
size=1)
mock.ReplayAll()
drv._use_image_utils_to_pipe_bytes_to_volume(
context, volume, "image_service", "image_id")
mock.VerifyAll()
def test_use_glance_plugin_to_copy_image_to_volume_success(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
volume = dict(
provider_location='sr-uuid/vdi-uuid',
size=2)
mock.StubOutWithMock(driver.glance, 'get_api_servers')
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
drv.nfs_ops.use_glance_plugin_to_overwrite_volume(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
'image_id', 'token', '/var/run/sr-mount').AndReturn(True)
drv.nfs_ops.resize_volume(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 2)
mock.ReplayAll()
drv._use_glance_plugin_to_copy_image_to_volume(
MockContext('token'), volume, "ignore", "image_id")
mock.VerifyAll()
def test_use_glance_plugin_to_copy_image_to_volume_fail(self):
mock, drv = self._setup_mock_driver(
'server', 'serverpath', '/var/run/sr-mount')
volume = dict(
provider_location='sr-uuid/vdi-uuid',
size=2)
mock.StubOutWithMock(driver.glance, 'get_api_servers')
driver.glance.get_api_servers().AndReturn((x for x in ['glancesrv']))
drv.nfs_ops.use_glance_plugin_to_overwrite_volume(
'server', 'serverpath', 'sr-uuid', 'vdi-uuid', 'glancesrv',
'image_id', 'token', '/var/run/sr-mount').AndReturn(False)
mock.ReplayAll()
self.assertRaises(
exception.ImageCopyFailure,
lambda: drv._use_glance_plugin_to_copy_image_to_volume(
MockContext('token'), volume, "ignore", "image_id"))
mock.VerifyAll()
def test_get_volume_stats_reports_required_keys(self):
drv = get_configured_driver()
stats = drv.get_volume_stats()
required_metrics = [
'volume_backend_name', 'vendor_name', 'driver_version',
'storage_protocol', 'total_capacity_gb', 'free_capacity_gb',
'reserved_percentage'
]
for metric in required_metrics:
self.assertIn(metric, stats)
def test_get_volume_stats_reports_unknown_cap(self):
drv = get_configured_driver()
stats = drv.get_volume_stats()
self.assertEqual('unknown', stats['free_capacity_gb'])
def test_reported_driver_type(self):
drv = get_configured_driver()
stats = drv.get_volume_stats()
self.assertEqual('xensm', stats['storage_protocol'])
class ToolsTest(test.TestCase):
@mock.patch('cinder.volume.drivers.xenapi.tools._stripped_first_line_of')
def test_get_this_vm_uuid(self, mock_read_first_line):
mock_read_first_line.return_value = 'someuuid'
self.assertEqual('someuuid', tools.get_this_vm_uuid())
mock_read_first_line.assert_called_once_with('/sys/hypervisor/uuid')
def test_stripped_first_line_of(self):
mock_context_manager = mock.Mock()
mock_context_manager.__enter__ = mock.Mock(
return_value=six.StringIO(' blah \n second line \n'))
mock_context_manager.__exit__ = mock.Mock(return_value=False)
mock_open = mock.Mock(return_value=mock_context_manager)
with mock.patch('__builtin__.open', mock_open):
self.assertEqual(
'blah', tools._stripped_first_line_of('/somefile'))
mock_open.assert_called_once_with('/somefile', 'rb')

View File

@ -1,542 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import os
import pickle
from cinder.openstack.common import units
from cinder.volume.drivers.xenapi import tools
class XenAPIException(Exception):
def __init__(self, original_exception):
super(XenAPIException, self).__init__(original_exception)
self.original_exception = original_exception
class OperationsBase(object):
def __init__(self, xenapi_session):
self.session = xenapi_session
def call_xenapi(self, method, *args):
return self.session.call_xenapi(method, *args)
class VMOperations(OperationsBase):
def get_by_uuid(self, vm_uuid):
return self.call_xenapi('VM.get_by_uuid', vm_uuid)
def get_vbds(self, vm_uuid):
return self.call_xenapi('VM.get_VBDs', vm_uuid)
class VBDOperations(OperationsBase):
def create(self, vm_ref, vdi_ref, userdevice, bootable, mode, type,
empty, other_config):
vbd_rec = dict(
VM=vm_ref,
VDI=vdi_ref,
userdevice=str(userdevice),
bootable=bootable,
mode=mode,
type=type,
empty=empty,
other_config=other_config,
qos_algorithm_type='',
qos_algorithm_params=dict()
)
return self.call_xenapi('VBD.create', vbd_rec)
def destroy(self, vbd_ref):
self.call_xenapi('VBD.destroy', vbd_ref)
def get_device(self, vbd_ref):
return self.call_xenapi('VBD.get_device', vbd_ref)
def plug(self, vbd_ref):
return self.call_xenapi('VBD.plug', vbd_ref)
def unplug(self, vbd_ref):
return self.call_xenapi('VBD.unplug', vbd_ref)
def get_vdi(self, vbd_ref):
return self.call_xenapi('VBD.get_VDI', vbd_ref)
class PoolOperations(OperationsBase):
def get_all(self):
return self.call_xenapi('pool.get_all')
def get_default_SR(self, pool_ref):
return self.call_xenapi('pool.get_default_SR', pool_ref)
class PbdOperations(OperationsBase):
def get_all(self):
return self.call_xenapi('PBD.get_all')
def unplug(self, pbd_ref):
self.call_xenapi('PBD.unplug', pbd_ref)
def create(self, host_ref, sr_ref, device_config):
return self.call_xenapi(
'PBD.create',
dict(
host=host_ref,
SR=sr_ref,
device_config=device_config
)
)
def plug(self, pbd_ref):
self.call_xenapi('PBD.plug', pbd_ref)
class SrOperations(OperationsBase):
def get_all(self):
return self.call_xenapi('SR.get_all')
def get_record(self, sr_ref):
return self.call_xenapi('SR.get_record', sr_ref)
def forget(self, sr_ref):
self.call_xenapi('SR.forget', sr_ref)
def scan(self, sr_ref):
self.call_xenapi('SR.scan', sr_ref)
def create(self, host_ref, device_config, name_label, name_description,
sr_type, physical_size=None, content_type=None,
shared=False, sm_config=None):
return self.call_xenapi(
'SR.create',
host_ref,
device_config,
physical_size or '0',
name_label or '',
name_description or '',
sr_type,
content_type or '',
shared,
sm_config or dict()
)
def introduce(self, sr_uuid, name_label, name_description, sr_type,
content_type=None, shared=False, sm_config=None):
return self.call_xenapi(
'SR.introduce',
sr_uuid,
name_label or '',
name_description or '',
sr_type,
content_type or '',
shared,
sm_config or dict()
)
def get_uuid(self, sr_ref):
return self.get_record(sr_ref)['uuid']
def get_name_label(self, sr_ref):
return self.get_record(sr_ref)['name_label']
def get_name_description(self, sr_ref):
return self.get_record(sr_ref)['name_description']
def destroy(self, sr_ref):
self.call_xenapi('SR.destroy', sr_ref)
class VdiOperations(OperationsBase):
def get_all(self):
return self.call_xenapi('VDI.get_all')
def get_record(self, vdi_ref):
return self.call_xenapi('VDI.get_record', vdi_ref)
def get_by_uuid(self, vdi_uuid):
return self.call_xenapi('VDI.get_by_uuid', vdi_uuid)
def get_uuid(self, vdi_ref):
return self.get_record(vdi_ref)['uuid']
def create(self, sr_ref, size, vdi_type,
sharable=False, read_only=False, other_config=None):
return self.call_xenapi('VDI.create',
dict(SR=sr_ref,
virtual_size=str(size),
type=vdi_type,
sharable=sharable,
read_only=read_only,
other_config=other_config or dict()))
def destroy(self, vdi_ref):
self.call_xenapi('VDI.destroy', vdi_ref)
def copy(self, vdi_ref, sr_ref):
return self.call_xenapi('VDI.copy', vdi_ref, sr_ref)
def resize(self, vdi_ref, size):
return self.call_xenapi('VDI.resize', vdi_ref, str(size))
class HostOperations(OperationsBase):
def get_record(self, host_ref):
return self.call_xenapi('host.get_record', host_ref)
def get_uuid(self, host_ref):
return self.get_record(host_ref)['uuid']
class XenAPISession(object):
def __init__(self, session, exception_to_convert):
self._session = session
self._exception_to_convert = exception_to_convert
self.handle = self._session.handle
self.PBD = PbdOperations(self)
self.SR = SrOperations(self)
self.VDI = VdiOperations(self)
self.host = HostOperations(self)
self.pool = PoolOperations(self)
self.VBD = VBDOperations(self)
self.VM = VMOperations(self)
def close(self):
return self.call_xenapi('logout')
@contextlib.contextmanager
def exception_converter(self):
try:
yield None
except self._exception_to_convert as e:
raise XenAPIException(e)
def call_xenapi(self, method, *args):
with self.exception_converter():
return self._session.xenapi_request(method, args)
def call_plugin(self, host_ref, plugin, function, args):
with self.exception_converter():
return self._session.xenapi.host.call_plugin(
host_ref, plugin, function, args)
def get_pool(self):
return self.call_xenapi('session.get_pool', self.handle)
def get_this_host(self):
return self.call_xenapi('session.get_this_host', self.handle)
class CompoundOperations(object):
def unplug_pbds_from_sr(self, sr_ref):
sr_rec = self.SR.get_record(sr_ref)
for pbd_ref in sr_rec.get('PBDs', []):
self.PBD.unplug(pbd_ref)
def unplug_pbds_and_forget_sr(self, sr_ref):
self.unplug_pbds_from_sr(sr_ref)
self.SR.forget(sr_ref)
def create_new_vdi(self, sr_ref, size_in_gigabytes):
return self.VDI.create(sr_ref,
to_bytes(size_in_gigabytes),
'User', )
def to_bytes(size_in_gigs):
return size_in_gigs * units.Gi
class NFSOperationsMixIn(CompoundOperations):
def is_nfs_sr(self, sr_ref):
return self.SR.get_record(sr_ref).get('type') == 'nfs'
@contextlib.contextmanager
def new_sr_on_nfs(self, host_ref, server, serverpath,
name_label=None, name_description=None):
device_config = dict(
server=server,
serverpath=serverpath
)
name_label = name_label or ''
name_description = name_description or ''
sr_type = 'nfs'
sr_ref = self.SR.create(
host_ref,
device_config,
name_label,
name_description,
sr_type,
)
yield sr_ref
self.unplug_pbds_and_forget_sr(sr_ref)
def plug_nfs_sr(self, host_ref, server, serverpath, sr_uuid,
name_label=None, name_description=None):
device_config = dict(
server=server,
serverpath=serverpath
)
sr_type = 'nfs'
sr_ref = self.SR.introduce(
sr_uuid,
name_label,
name_description,
sr_type,
)
pbd_ref = self.PBD.create(
host_ref,
sr_ref,
device_config
)
self.PBD.plug(pbd_ref)
return sr_ref
def connect_volume(self, server, serverpath, sr_uuid, vdi_uuid):
host_ref = self.get_this_host()
sr_ref = self.plug_nfs_sr(
host_ref,
server,
serverpath,
sr_uuid
)
self.SR.scan(sr_ref)
vdi_ref = self.VDI.get_by_uuid(vdi_uuid)
return dict(sr_ref=sr_ref, vdi_ref=vdi_ref)
def copy_vdi_to_sr(self, vdi_ref, sr_ref):
return self.VDI.copy(vdi_ref, sr_ref)
class ContextAwareSession(XenAPISession):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
class OpenStackXenAPISession(ContextAwareSession,
NFSOperationsMixIn):
pass
def connect(url, user, password):
import XenAPI
session = XenAPI.Session(url)
session.login_with_password(user, password)
return OpenStackXenAPISession(session, XenAPI.Failure)
class SessionFactory(object):
def __init__(self, url, user, password):
self.url = url
self.user = user
self.password = password
def get_session(self):
return connect(self.url, self.user, self.password)
class XapiPluginProxy(object):
def __init__(self, session_factory, plugin_name):
self._session_factory = session_factory
self._plugin_name = plugin_name
def call(self, function, *plugin_args, **plugin_kwargs):
plugin_params = dict(args=plugin_args, kwargs=plugin_kwargs)
args = dict(params=pickle.dumps(plugin_params))
with self._session_factory.get_session() as session:
host_ref = session.get_this_host()
result = session.call_plugin(
host_ref, self._plugin_name, function, args)
return pickle.loads(result)
class GlancePluginProxy(XapiPluginProxy):
def __init__(self, session_factory):
super(GlancePluginProxy, self).__init__(session_factory, 'glance')
def download_vhd(self, image_id, glance_host, glance_port, glance_use_ssl,
uuid_stack, sr_path, auth_token):
return self.call(
'download_vhd',
image_id=image_id,
glance_host=glance_host,
glance_port=glance_port,
glance_use_ssl=glance_use_ssl,
uuid_stack=uuid_stack,
sr_path=sr_path,
auth_token=auth_token)
def upload_vhd(self, vdi_uuids, image_id, glance_host, glance_port,
glance_use_ssl, sr_path, auth_token, properties):
return self.call(
'upload_vhd',
vdi_uuids=vdi_uuids,
image_id=image_id,
glance_host=glance_host,
glance_port=glance_port,
glance_use_ssl=glance_use_ssl,
sr_path=sr_path,
auth_token=auth_token,
properties=properties)
class NFSBasedVolumeOperations(object):
def __init__(self, session_factory):
self._session_factory = session_factory
self.glance_plugin = GlancePluginProxy(session_factory)
def create_volume(self, server, serverpath, size,
name=None, description=None):
with self._session_factory.get_session() as session:
host_ref = session.get_this_host()
with session.new_sr_on_nfs(host_ref, server, serverpath,
name, description) as sr_ref:
vdi_ref = session.create_new_vdi(sr_ref, size)
return dict(
sr_uuid=session.SR.get_uuid(sr_ref),
vdi_uuid=session.VDI.get_uuid(vdi_ref)
)
def delete_volume(self, server, serverpath, sr_uuid, vdi_uuid):
with self._session_factory.get_session() as session:
refs = session.connect_volume(
server, serverpath, sr_uuid, vdi_uuid)
session.VDI.destroy(refs['vdi_ref'])
sr_ref = refs['sr_ref']
session.unplug_pbds_from_sr(sr_ref)
session.SR.destroy(sr_ref)
def connect_volume(self, server, serverpath, sr_uuid, vdi_uuid):
with self._session_factory.get_session() as session:
refs = session.connect_volume(
server, serverpath, sr_uuid, vdi_uuid)
return session.VDI.get_uuid(refs['vdi_ref'])
def disconnect_volume(self, vdi_uuid):
with self._session_factory.get_session() as session:
vdi_ref = session.VDI.get_by_uuid(vdi_uuid)
vdi_rec = session.VDI.get_record(vdi_ref)
sr_ref = vdi_rec['SR']
session.unplug_pbds_and_forget_sr(sr_ref)
def copy_volume(self, server, serverpath, sr_uuid, vdi_uuid,
name=None, description=None):
with self._session_factory.get_session() as session:
src_refs = session.connect_volume(
server, serverpath, sr_uuid, vdi_uuid)
try:
host_ref = session.get_this_host()
with session.new_sr_on_nfs(host_ref, server, serverpath,
name, description) as target_sr_ref:
target_vdi_ref = session.copy_vdi_to_sr(
src_refs['vdi_ref'], target_sr_ref)
dst_refs = dict(
sr_uuid=session.SR.get_uuid(target_sr_ref),
vdi_uuid=session.VDI.get_uuid(target_vdi_ref)
)
finally:
session.unplug_pbds_and_forget_sr(src_refs['sr_ref'])
return dst_refs
def resize_volume(self, server, serverpath, sr_uuid, vdi_uuid,
size_in_gigabytes):
self.connect_volume(server, serverpath, sr_uuid, vdi_uuid)
try:
with self._session_factory.get_session() as session:
vdi_ref = session.VDI.get_by_uuid(vdi_uuid)
session.VDI.resize(vdi_ref, to_bytes(size_in_gigabytes))
finally:
self.disconnect_volume(vdi_uuid)
def use_glance_plugin_to_overwrite_volume(self, server, serverpath,
sr_uuid, vdi_uuid, glance_server,
image_id, auth_token,
sr_base_path):
self.connect_volume(server, serverpath, sr_uuid, vdi_uuid)
uuid_stack = [vdi_uuid]
glance_host, glance_port, glance_use_ssl = glance_server
try:
result = self.glance_plugin.download_vhd(
image_id, glance_host, glance_port, glance_use_ssl, uuid_stack,
os.path.join(sr_base_path, sr_uuid), auth_token)
finally:
self.disconnect_volume(vdi_uuid)
if len(result) != 1 or result['root']['uuid'] != vdi_uuid:
return False
return True
def use_glance_plugin_to_upload_volume(self, server, serverpath,
sr_uuid, vdi_uuid, glance_server,
image_id, auth_token, sr_base_path):
self.connect_volume(server, serverpath, sr_uuid, vdi_uuid)
vdi_uuids = [vdi_uuid]
glance_host, glance_port, glance_use_ssl = glance_server
try:
self.glance_plugin.upload_vhd(
vdi_uuids, image_id, glance_host, glance_port, glance_use_ssl,
os.path.join(sr_base_path, sr_uuid), auth_token, dict())
finally:
self.disconnect_volume(vdi_uuid)
@contextlib.contextmanager
def volume_attached_here(self, server, serverpath, sr_uuid, vdi_uuid,
readonly=True):
self.connect_volume(server, serverpath, sr_uuid, vdi_uuid)
with self._session_factory.get_session() as session:
vm_uuid = tools.get_this_vm_uuid()
vm_ref = session.VM.get_by_uuid(vm_uuid)
vdi_ref = session.VDI.get_by_uuid(vdi_uuid)
vbd_ref = session.VBD.create(
vm_ref, vdi_ref, userdevice='autodetect', bootable=False,
mode='RO' if readonly else 'RW', type='disk', empty=False,
other_config=dict())
session.VBD.plug(vbd_ref)
device = session.VBD.get_device(vbd_ref)
try:
yield "/dev/" + device
finally:
session.VBD.unplug(vbd_ref)
session.VBD.destroy(vbd_ref)
self.disconnect_volume(vdi_uuid)

View File

@ -1,270 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.config import cfg
from cinder import exception
from cinder.image import glance
from cinder.image import image_utils
from cinder.openstack.common import log as logging
from cinder.volume import driver
from cinder.volume.drivers.xenapi import lib as xenapi_lib
LOG = logging.getLogger(__name__)
xenapi_opts = [
cfg.StrOpt('xenapi_connection_url',
default=None,
help='URL for XenAPI connection'),
cfg.StrOpt('xenapi_connection_username',
default='root',
help='Username for XenAPI connection'),
cfg.StrOpt('xenapi_connection_password',
default=None,
help='Password for XenAPI connection',
secret=True),
cfg.StrOpt('xenapi_sr_base_path',
default='/var/run/sr-mount',
help='Base path to the storage repository'),
]
xenapi_nfs_opts = [
cfg.StrOpt('xenapi_nfs_server',
default=None,
help='NFS server to be used by XenAPINFSDriver'),
cfg.StrOpt('xenapi_nfs_serverpath',
default=None,
help='Path of exported NFS, used by XenAPINFSDriver'),
]
CONF = cfg.CONF
CONF.register_opts(xenapi_opts)
CONF.register_opts(xenapi_nfs_opts)
class XenAPINFSDriver(driver.VolumeDriver):
VERSION = "1.0.0"
def __init__(self, *args, **kwargs):
super(XenAPINFSDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(xenapi_opts)
self.configuration.append_config_values(xenapi_nfs_opts)
def do_setup(self, context):
session_factory = xenapi_lib.SessionFactory(
self.configuration.xenapi_connection_url,
self.configuration.xenapi_connection_username,
self.configuration.xenapi_connection_password
)
self.nfs_ops = xenapi_lib.NFSBasedVolumeOperations(session_factory)
def create_cloned_volume(self, volume, src_vref):
raise NotImplementedError()
def create_volume(self, volume):
volume_details = self.nfs_ops.create_volume(
self.configuration.xenapi_nfs_server,
self.configuration.xenapi_nfs_serverpath,
volume['size'],
volume['display_name'],
volume['display_description']
)
location = "%(sr_uuid)s/%(vdi_uuid)s" % volume_details
return dict(provider_location=location)
def create_export(self, context, volume):
pass
def delete_volume(self, volume):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
self.nfs_ops.delete_volume(
self.configuration.xenapi_nfs_server,
self.configuration.xenapi_nfs_serverpath,
sr_uuid,
vdi_uuid
)
def remove_export(self, context, volume):
pass
def initialize_connection(self, volume, connector):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
return dict(
driver_volume_type='xensm',
data=dict(
name_label=volume['display_name'] or '',
name_description=volume['display_description'] or '',
sr_uuid=sr_uuid,
vdi_uuid=vdi_uuid,
sr_type='nfs',
server=self.configuration.xenapi_nfs_server,
serverpath=self.configuration.xenapi_nfs_serverpath,
introduce_sr_keys=['sr_type', 'server', 'serverpath']
)
)
def terminate_connection(self, volume, connector, **kwargs):
pass
def check_for_setup_error(self):
"""To override superclass' method."""
def create_volume_from_snapshot(self, volume, snapshot):
return self._copy_volume(
snapshot, volume['display_name'], volume['name_description'])
def create_snapshot(self, snapshot):
volume = snapshot['volume']
return self._copy_volume(
volume, snapshot['display_name'], snapshot['display_description'])
def _copy_volume(self, volume, target_name, target_desc):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
volume_details = self.nfs_ops.copy_volume(
self.configuration.xenapi_nfs_server,
self.configuration.xenapi_nfs_serverpath,
sr_uuid,
vdi_uuid,
target_name,
target_desc
)
location = "%(sr_uuid)s/%(vdi_uuid)s" % volume_details
return dict(provider_location=location)
def delete_snapshot(self, snapshot):
self.delete_volume(snapshot)
def ensure_export(self, context, volume):
pass
def copy_image_to_volume(self, context, volume, image_service, image_id):
if image_utils.is_xenserver_image(context, image_service, image_id):
return self._use_glance_plugin_to_copy_image_to_volume(
context, volume, image_service, image_id)
return self._use_image_utils_to_pipe_bytes_to_volume(
context, volume, image_service, image_id)
def _use_image_utils_to_pipe_bytes_to_volume(self, context, volume,
image_service, image_id):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
with self.nfs_ops.volume_attached_here(CONF.xenapi_nfs_server,
CONF.xenapi_nfs_serverpath,
sr_uuid, vdi_uuid,
False) as device:
image_utils.fetch_to_raw(context,
image_service,
image_id,
device,
self.configuration.volume_dd_blocksize,
size=volume['size'])
def _use_glance_plugin_to_copy_image_to_volume(self, context, volume,
image_service, image_id):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
api_servers = glance.get_api_servers()
glance_server = api_servers.next()
auth_token = context.auth_token
overwrite_result = self.nfs_ops.use_glance_plugin_to_overwrite_volume(
CONF.xenapi_nfs_server,
CONF.xenapi_nfs_serverpath,
sr_uuid,
vdi_uuid,
glance_server,
image_id,
auth_token,
CONF.xenapi_sr_base_path)
if overwrite_result is False:
raise exception.ImageCopyFailure(reason='Overwriting volume '
'failed.')
self.nfs_ops.resize_volume(
CONF.xenapi_nfs_server,
CONF.xenapi_nfs_serverpath,
sr_uuid,
vdi_uuid,
volume['size'])
def copy_volume_to_image(self, context, volume, image_service, image_meta):
if image_utils.is_xenserver_format(image_meta):
return self._use_glance_plugin_to_upload_volume(
context, volume, image_service, image_meta)
return self._use_image_utils_to_upload_volume(
context, volume, image_service, image_meta)
def _use_image_utils_to_upload_volume(self, context, volume, image_service,
image_meta):
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
with self.nfs_ops.volume_attached_here(CONF.xenapi_nfs_server,
CONF.xenapi_nfs_serverpath,
sr_uuid, vdi_uuid,
True) as device:
image_utils.upload_volume(context,
image_service,
image_meta,
device)
def _use_glance_plugin_to_upload_volume(self, context, volume,
image_service, image_meta):
image_id = image_meta['id']
sr_uuid, vdi_uuid = volume['provider_location'].split('/')
api_servers = glance.get_api_servers()
glance_server = api_servers.next()
auth_token = context.auth_token
self.nfs_ops.use_glance_plugin_to_upload_volume(
CONF.xenapi_nfs_server,
CONF.xenapi_nfs_serverpath,
sr_uuid,
vdi_uuid,
glance_server,
image_id,
auth_token,
CONF.xenapi_sr_base_path)
def get_volume_stats(self, refresh=False):
if refresh or not self._stats:
data = {}
backend_name = self.configuration.safe_get('volume_backend_name')
data["volume_backend_name"] = backend_name or 'XenAPINFS',
data['vendor_name'] = 'Open Source',
data['driver_version'] = self.VERSION
data['storage_protocol'] = 'xensm'
data['total_capacity_gb'] = 'unknown'
data['free_capacity_gb'] = 'unknown'
data['reserved_percentage'] = 0
self._stats = data
return self._stats
def backup_volume(self, context, backup, backup_service):
"""Create a new backup from an existing volume."""
raise NotImplementedError()
def restore_backup(self, context, backup, volume, backup_service):
"""Restore an existing backup to a new or existing volume."""
raise NotImplementedError()

View File

@ -1,7 +0,0 @@
def _stripped_first_line_of(filename):
with open(filename, 'rb') as f:
return f.readline().strip()
def get_this_vm_uuid():
return _stripped_first_line_of('/sys/hypervisor/uuid')

View File

@ -2152,29 +2152,6 @@
#windows_iscsi_lun_path=C:\iSCSIVirtualDisks
#
# Options defined in cinder.volume.drivers.xenapi.sm
#
# NFS server to be used by XenAPINFSDriver (string value)
#xenapi_nfs_server=<None>
# Path of exported NFS, used by XenAPINFSDriver (string value)
#xenapi_nfs_serverpath=<None>
# URL for XenAPI connection (string value)
#xenapi_connection_url=<None>
# Username for XenAPI connection (string value)
#xenapi_connection_username=root
# Password for XenAPI connection (string value)
#xenapi_connection_password=<None>
# Base path to the storage repository (string value)
#xenapi_sr_base_path=/var/run/sr-mount
#
# Options defined in cinder.volume.drivers.zadara
#