Experimental: File I/O Volume Driver Implementation
Implements the volume driver that supports file backed cinder volumes. Resolves long standing issue with how our volume adapters are created and now allows for a single host to support multiple different volume types at once. This is considered experimental. Full support should land in Pike. To use, the NovaLink nightly builds must be utilized. Change-Id: Ia78d2ed17232cc0f42fed1391786384e4dea28c1 Partially-Implements: bp/file-io-cinder-connector
This commit is contained in:
parent
8651e2213c
commit
ebee3eae88
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2016 IBM Corp.
|
# Copyright 2016, 2017 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@ -24,7 +24,6 @@ import nova_powervm.virt.powervm.driver as real_drv
|
|||||||
|
|
||||||
LOG = real_drv.LOG
|
LOG = real_drv.LOG
|
||||||
CONF = real_drv.CONF
|
CONF = real_drv.CONF
|
||||||
VOLUME_DRIVER_MAPPINGS = real_drv.VOLUME_DRIVER_MAPPINGS
|
|
||||||
DISK_ADPT_NS = real_drv.DISK_ADPT_NS
|
DISK_ADPT_NS = real_drv.DISK_ADPT_NS
|
||||||
DISK_ADPT_MAPPINGS = real_drv.DISK_ADPT_MAPPINGS
|
DISK_ADPT_MAPPINGS = real_drv.DISK_ADPT_MAPPINGS
|
||||||
NVRAM_NS = real_drv.NVRAM_NS
|
NVRAM_NS = real_drv.NVRAM_NS
|
||||||
|
@ -116,7 +116,7 @@ ssp_opts = [
|
|||||||
vol_adapter_opts = [
|
vol_adapter_opts = [
|
||||||
cfg.StrOpt('fc_attach_strategy',
|
cfg.StrOpt('fc_attach_strategy',
|
||||||
choices=['vscsi', 'npiv'], ignore_case=True,
|
choices=['vscsi', 'npiv'], ignore_case=True,
|
||||||
default='vscsi',
|
default='vscsi', mutable=True,
|
||||||
help='The Fibre Channel Volume Strategy defines how FC Cinder '
|
help='The Fibre Channel Volume Strategy defines how FC Cinder '
|
||||||
'volumes should be attached to the Virtual Machine. The '
|
'volumes should be attached to the Virtual Machine. The '
|
||||||
'options are: npiv or vscsi. If npiv is selected then '
|
'options are: npiv or vscsi. If npiv is selected then '
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2014, 2016 IBM Corp.
|
# Copyright 2014, 2017 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@ -13,7 +13,6 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
@ -51,7 +50,6 @@ from nova_powervm.tests.virt.powervm import fixtures as fx
|
|||||||
from nova_powervm.virt.powervm import exception as p_exc
|
from nova_powervm.virt.powervm import exception as p_exc
|
||||||
from nova_powervm.virt.powervm import live_migration as lpm
|
from nova_powervm.virt.powervm import live_migration as lpm
|
||||||
from nova_powervm.virt.powervm import vm
|
from nova_powervm.virt.powervm import vm
|
||||||
from nova_powervm.virt.powervm import volume as vol_attach
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
@ -162,15 +160,6 @@ class TestPowerVMDriver(test.TestCase):
|
|||||||
self.lpm_inst.uuid = 'inst1'
|
self.lpm_inst.uuid = 'inst1'
|
||||||
self.drv.live_migrations = {'inst1': self.lpm}
|
self.drv.live_migrations = {'inst1': self.lpm}
|
||||||
|
|
||||||
def _vol_drv_maps(self):
|
|
||||||
VOLUME_DRIVER_MAPPINGS = {
|
|
||||||
'fibre_channel': vol_attach.FC_STRATEGY_MAPPING[
|
|
||||||
driver.CONF.powervm.fc_attach_strategy.lower()],
|
|
||||||
'iscsi': vol_attach.NETWORK_STRATEGY_MAPPING[
|
|
||||||
driver.CONF.powervm.network_attach_strategy.lower()],
|
|
||||||
}
|
|
||||||
return VOLUME_DRIVER_MAPPINGS
|
|
||||||
|
|
||||||
def test_driver_create(self):
|
def test_driver_create(self):
|
||||||
"""Validates that a driver of the PowerVM type can be initialized."""
|
"""Validates that a driver of the PowerVM type can be initialized."""
|
||||||
test_drv = driver.PowerVMDriver(fake.FakeVirtAPI())
|
test_drv = driver.PowerVMDriver(fake.FakeVirtAPI())
|
||||||
@ -863,44 +852,40 @@ class TestPowerVMDriver(test.TestCase):
|
|||||||
ret = self.drv._is_booted_from_volume(None)
|
ret = self.drv._is_booted_from_volume(None)
|
||||||
self.assertFalse(ret)
|
self.assertFalse(ret)
|
||||||
|
|
||||||
@mock.patch('nova_powervm.virt.powervm.driver.VOLUME_DRIVER_MAPPINGS')
|
def test_get_inst_xag(self):
|
||||||
def test_get_inst_xag(self, mock_mapping):
|
|
||||||
def getitem(name):
|
|
||||||
return self._vol_drv_maps()[name]
|
|
||||||
mock_mapping.__getitem__.side_effect = getitem
|
|
||||||
|
|
||||||
self.flags(volume_adapter='fibre_channel', group='powervm')
|
|
||||||
# No volumes - should be just the SCSI mapping
|
# No volumes - should be just the SCSI mapping
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), None)
|
xag = self.drv._get_inst_xag(mock.Mock(), None)
|
||||||
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
||||||
|
|
||||||
# The vSCSI Volume attach - only needs the SCSI mapping.
|
# The vSCSI Volume attach - only needs the SCSI mapping.
|
||||||
self.flags(fc_attach_strategy='vscsi', group='powervm')
|
self.flags(fc_attach_strategy='vscsi', group='powervm')
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
|
mock_bdm = {'connection_info':
|
||||||
|
{'driver_volume_type': 'fibre_channel'}}
|
||||||
|
xag = self.drv._get_inst_xag(mock.Mock(), [mock_bdm])
|
||||||
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
||||||
|
|
||||||
# The NPIV volume attach - requires SCSI, Storage and FC Mapping
|
# The NPIV volume attach - requires SCSI, Storage and FC Mapping
|
||||||
self.flags(fc_attach_strategy='npiv', group='powervm')
|
self.flags(fc_attach_strategy='npiv', group='powervm')
|
||||||
mock_mapping.return_value = self._vol_drv_maps()
|
xag = self.drv._get_inst_xag(mock.Mock(), [mock_bdm])
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
|
self.assertEqual({pvm_const.XAG.VIO_STOR, pvm_const.XAG.VIO_SMAP,
|
||||||
self.assertEqual({pvm_const.XAG.VIO_STOR,
|
|
||||||
pvm_const.XAG.VIO_SMAP,
|
|
||||||
pvm_const.XAG.VIO_FMAP}, set(xag))
|
pvm_const.XAG.VIO_FMAP}, set(xag))
|
||||||
|
|
||||||
# The vSCSI Volume attach - Ensure case insensitive.
|
# The vSCSI Volume attach - Ensure case insensitive.
|
||||||
self.flags(fc_attach_strategy='VSCSI', group='powervm')
|
self.flags(fc_attach_strategy='VSCSI', group='powervm')
|
||||||
mock_mapping.return_value = self._vol_drv_maps()
|
xag = self.drv._get_inst_xag(mock.Mock(), [mock_bdm])
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
|
|
||||||
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
||||||
|
|
||||||
# The iSCSI volume attach - only nees the SCSI mapping.
|
# Validate the other volume types only return SCSI mappings
|
||||||
self.flags(volume_adapter='iscsi', group='powervm')
|
vol_types = ['iscsi', 'gpfs', 'local', 'nfs']
|
||||||
mock_mapping.return_value = self._vol_drv_maps()
|
for vol_type in vol_types:
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
|
self.flags(volume_adapter='iscsi', group='powervm')
|
||||||
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
mock_bdm = {'connection_info':
|
||||||
|
{'driver_volume_type': vol_type}}
|
||||||
|
xag = self.drv._get_inst_xag(mock.Mock(), [mock_bdm])
|
||||||
|
self.assertEqual([pvm_const.XAG.VIO_SMAP], xag)
|
||||||
|
|
||||||
# If a recreate, all should be returned
|
# If a recreate, all should be returned
|
||||||
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()], recreate=True)
|
xag = self.drv._get_inst_xag(mock.Mock(), [mock_bdm], recreate=True)
|
||||||
self.assertEqual({pvm_const.XAG.VIO_STOR,
|
self.assertEqual({pvm_const.XAG.VIO_STOR,
|
||||||
pvm_const.XAG.VIO_SMAP,
|
pvm_const.XAG.VIO_SMAP,
|
||||||
pvm_const.XAG.VIO_FMAP}, set(xag))
|
pvm_const.XAG.VIO_FMAP}, set(xag))
|
||||||
@ -2041,8 +2026,8 @@ class TestPowerVMDriver(test.TestCase):
|
|||||||
|
|
||||||
def _get_results(block_device_info=None, bdms=None):
|
def _get_results(block_device_info=None, bdms=None):
|
||||||
# Patch so we get the same mock back each time.
|
# Patch so we get the same mock back each time.
|
||||||
with mock.patch.object(self.drv, '_get_inst_vol_adpt',
|
with mock.patch('nova_powervm.virt.powervm.volume.'
|
||||||
return_value=vol_adpt):
|
'build_volume_driver', return_value=vol_adpt):
|
||||||
return [
|
return [
|
||||||
(bdm, vol_drv) for bdm, vol_drv in self.drv._vol_drv_iter(
|
(bdm, vol_drv) for bdm, vol_drv in self.drv._vol_drv_iter(
|
||||||
'context', self.inst,
|
'context', self.inst,
|
||||||
|
@ -16,10 +16,17 @@
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||||
|
import six
|
||||||
|
|
||||||
from nova import test
|
from nova import test
|
||||||
|
|
||||||
from nova_powervm.virt.powervm import volume
|
from nova_powervm.virt.powervm import volume
|
||||||
|
from nova_powervm.virt.powervm.volume import gpfs
|
||||||
|
from nova_powervm.virt.powervm.volume import iscsi
|
||||||
|
from nova_powervm.virt.powervm.volume import local
|
||||||
|
from nova_powervm.virt.powervm.volume import nfs
|
||||||
|
from nova_powervm.virt.powervm.volume import npiv
|
||||||
|
from nova_powervm.virt.powervm.volume import vscsi
|
||||||
|
|
||||||
|
|
||||||
class TestVolumeAdapter(test.TestCase):
|
class TestVolumeAdapter(test.TestCase):
|
||||||
@ -39,6 +46,14 @@ class TestVolumeAdapter(test.TestCase):
|
|||||||
|
|
||||||
class TestInitMethods(test.TestCase):
|
class TestInitMethods(test.TestCase):
|
||||||
|
|
||||||
|
# Volume driver types to classes
|
||||||
|
volume_drivers = {
|
||||||
|
'iscsi': iscsi.IscsiVolumeAdapter,
|
||||||
|
'local': local.LocalVolumeAdapter,
|
||||||
|
'nfs': nfs.NFSVolumeAdapter,
|
||||||
|
'gpfs': gpfs.GPFSVolumeAdapter,
|
||||||
|
}
|
||||||
|
|
||||||
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi_initiator')
|
@mock.patch('pypowervm.tasks.hdisk.discover_iscsi_initiator')
|
||||||
@mock.patch('pypowervm.tasks.partition.get_mgmt_partition')
|
@mock.patch('pypowervm.tasks.partition.get_mgmt_partition')
|
||||||
def test_get_iscsi_initiator(self, mock_mgmt, mock_iscsi_init):
|
def test_get_iscsi_initiator(self, mock_mgmt, mock_iscsi_init):
|
||||||
@ -61,3 +76,58 @@ class TestInitMethods(test.TestCase):
|
|||||||
self.assertEqual('test_initiator',
|
self.assertEqual('test_initiator',
|
||||||
volume.get_iscsi_initiator(mock_adpt))
|
volume.get_iscsi_initiator(mock_adpt))
|
||||||
self.assertEqual(1, mock_mgmt.call_count)
|
self.assertEqual(1, mock_mgmt.call_count)
|
||||||
|
|
||||||
|
def test_get_volume_class(self):
|
||||||
|
for vol_type, class_type in six.iteritems(self.volume_drivers):
|
||||||
|
self.assertEqual(class_type, volume.get_volume_class(vol_type))
|
||||||
|
|
||||||
|
# Try the fibre as vscsi
|
||||||
|
self.flags(fc_attach_strategy='vscsi', group='powervm')
|
||||||
|
self.assertEqual(vscsi.PVVscsiFCVolumeAdapter,
|
||||||
|
volume.get_volume_class('fibre_channel'))
|
||||||
|
|
||||||
|
# Try the fibre as npiv
|
||||||
|
self.flags(fc_attach_strategy='npiv', group='powervm')
|
||||||
|
self.assertEqual(npiv.NPIVVolumeAdapter,
|
||||||
|
volume.get_volume_class('fibre_channel'))
|
||||||
|
|
||||||
|
def test_build_volume_driver(self):
|
||||||
|
for vol_type, class_type in six.iteritems(self.volume_drivers):
|
||||||
|
vdrv = volume.build_volume_driver(
|
||||||
|
mock.Mock(), "abc123", mock.Mock(uuid='abc1'),
|
||||||
|
{'driver_volume_type': vol_type})
|
||||||
|
self.assertIsInstance(vdrv, class_type)
|
||||||
|
|
||||||
|
# Try the fibre as vscsi
|
||||||
|
self.flags(fc_attach_strategy='vscsi', group='powervm')
|
||||||
|
vdrv = volume.build_volume_driver(
|
||||||
|
mock.Mock(), "abc123", mock.Mock(uuid='abc1'),
|
||||||
|
{'driver_volume_type': 'fibre_channel'})
|
||||||
|
self.assertIsInstance(vdrv, vscsi.PVVscsiFCVolumeAdapter)
|
||||||
|
|
||||||
|
# Try the fibre as npiv
|
||||||
|
self.flags(fc_attach_strategy='npiv', group='powervm')
|
||||||
|
vdrv = volume.build_volume_driver(
|
||||||
|
mock.Mock(), "abc123", mock.Mock(uuid='abc1'),
|
||||||
|
{'driver_volume_type': 'fibre_channel'})
|
||||||
|
self.assertIsInstance(vdrv, npiv.NPIVVolumeAdapter)
|
||||||
|
|
||||||
|
def test_hostname_for_volume(self):
|
||||||
|
self.flags(host='test_host')
|
||||||
|
mock_instance = mock.Mock()
|
||||||
|
mock_instance.name = 'instance'
|
||||||
|
|
||||||
|
# Try the fibre as vscsi
|
||||||
|
self.flags(fc_attach_strategy='vscsi', group='powervm')
|
||||||
|
self.assertEqual("test_host",
|
||||||
|
volume.get_hostname_for_volume(mock_instance))
|
||||||
|
|
||||||
|
# Try the fibre as npiv
|
||||||
|
self.flags(fc_attach_strategy='npiv', group='powervm')
|
||||||
|
self.assertEqual("test_host_instance",
|
||||||
|
volume.get_hostname_for_volume(mock_instance))
|
||||||
|
|
||||||
|
# NPIV with long host name
|
||||||
|
self.flags(host='really_long_host_name_too_long')
|
||||||
|
self.assertEqual("really_long_host_nam_instance",
|
||||||
|
volume.get_hostname_for_volume(mock_instance))
|
||||||
|
99
nova_powervm/tests/virt/powervm/volume/test_fileio.py
Normal file
99
nova_powervm/tests/virt/powervm/volume/test_fileio.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 mock
|
||||||
|
from pypowervm import const as pvm_const
|
||||||
|
from pypowervm.tests import test_fixtures as pvm_fx
|
||||||
|
from pypowervm.wrappers import storage as pvm_stg
|
||||||
|
|
||||||
|
from nova_powervm.tests.virt.powervm.volume import test_driver as test_vol
|
||||||
|
from nova_powervm.virt.powervm.volume import fileio as v_drv
|
||||||
|
|
||||||
|
|
||||||
|
class FakeFileIOVolAdapter(v_drv.FileIOVolumeAdapter):
|
||||||
|
"""Subclass for FileIOVolumeAdapter, since it is abstract."""
|
||||||
|
|
||||||
|
def __init__(self, adapter, host_uuid, instance, connection_info,
|
||||||
|
stg_ftsk=None):
|
||||||
|
super(FakeFileIOVolAdapter, self).__init__(
|
||||||
|
adapter, host_uuid, instance, connection_info, stg_ftsk=stg_ftsk)
|
||||||
|
|
||||||
|
def _get_path(self):
|
||||||
|
return "fake_path"
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileIOVolumeAdapter(test_vol.TestVolumeAdapter):
|
||||||
|
"""Tests the FileIOVolumeAdapter. NovaLink is a I/O host."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestFileIOVolumeAdapter, self).setUp()
|
||||||
|
|
||||||
|
# Needed for the volume adapter
|
||||||
|
self.adpt = self.useFixture(pvm_fx.AdapterFx()).adpt
|
||||||
|
mock_inst = mock.MagicMock(uuid='2BC123')
|
||||||
|
|
||||||
|
self.vol_drv = FakeFileIOVolAdapter(self.adpt, 'host_uuid', mock_inst,
|
||||||
|
None)
|
||||||
|
|
||||||
|
self.mock_vio_task = mock.MagicMock()
|
||||||
|
self.mock_stg_ftsk = mock.MagicMock(
|
||||||
|
wrapper_tasks={'vios_uuid': self.mock_vio_task})
|
||||||
|
self.vol_drv.stg_ftsk = self.mock_stg_ftsk
|
||||||
|
|
||||||
|
def test_min_xags(self):
|
||||||
|
"""Ensures xag's only returns SCSI Mappings."""
|
||||||
|
self.assertEqual([pvm_const.XAG.VIO_SMAP], self.vol_drv.min_xags())
|
||||||
|
|
||||||
|
@mock.patch('pypowervm.tasks.partition.get_mgmt_partition')
|
||||||
|
@mock.patch('pypowervm.wrappers.storage.FileIO.bld')
|
||||||
|
def test_connect_volume(self, mock_file_bld, mock_get_mgmt_partition):
|
||||||
|
# Mockups
|
||||||
|
mock_file = mock.Mock()
|
||||||
|
mock_file_bld.return_value = mock_file
|
||||||
|
mock_slot_mgr = mock.MagicMock()
|
||||||
|
|
||||||
|
mock_vios = mock.Mock(uuid='vios_uuid')
|
||||||
|
mock_get_mgmt_partition.return_value = mock_vios
|
||||||
|
|
||||||
|
# Invoke
|
||||||
|
self.vol_drv._connect_volume(mock_slot_mgr)
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
mock_file_bld.assert_called_once_with(self.adpt, 'fake_path')
|
||||||
|
|
||||||
|
@mock.patch('pypowervm.tasks.partition.get_mgmt_partition')
|
||||||
|
@mock.patch('pypowervm.tasks.scsi_mapper.gen_match_func')
|
||||||
|
@mock.patch('pypowervm.tasks.scsi_mapper.find_maps')
|
||||||
|
def test_disconnect_volume(self, mock_find_maps, mock_gen_match_func,
|
||||||
|
mock_get_mgmt_partition):
|
||||||
|
# Mockups
|
||||||
|
mock_slot_mgr = mock.MagicMock()
|
||||||
|
|
||||||
|
mock_vios = mock.Mock(uuid='vios_uuid')
|
||||||
|
mock_get_mgmt_partition.return_value = mock_vios
|
||||||
|
|
||||||
|
mock_match_func = mock.Mock()
|
||||||
|
mock_gen_match_func.return_value = mock_match_func
|
||||||
|
|
||||||
|
# Invoke
|
||||||
|
self.vol_drv._disconnect_volume(mock_slot_mgr)
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
mock_gen_match_func.assert_called_once_with(
|
||||||
|
pvm_stg.FileIO, names=['fake_path'])
|
||||||
|
mock_find_maps.assert_called_once_with(
|
||||||
|
mock_vios.scsi_mappings, client_lpar_id='2BC123',
|
||||||
|
match_func=mock_match_func)
|
40
nova_powervm/tests/virt/powervm/volume/test_gpfs.py
Normal file
40
nova_powervm/tests/virt/powervm/volume/test_gpfs.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 mock
|
||||||
|
|
||||||
|
from nova_powervm.tests.virt.powervm.volume import test_driver as test_vol
|
||||||
|
from nova_powervm.virt.powervm.volume import gpfs as v_drv
|
||||||
|
|
||||||
|
|
||||||
|
class TestGPFSVolumeAdapter(test_vol.TestVolumeAdapter):
|
||||||
|
"""Tests the GPFSVolumeAdapter. NovaLink is a I/O host."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGPFSVolumeAdapter, self).setUp()
|
||||||
|
|
||||||
|
# Needed for the volume adapter
|
||||||
|
self.adpt = mock.Mock()
|
||||||
|
mock_inst = mock.MagicMock(uuid='2BC123')
|
||||||
|
|
||||||
|
# Connection Info
|
||||||
|
mock_conn_info = {'data': {'device_path': '/gpfs/path'}}
|
||||||
|
|
||||||
|
self.vol_drv = v_drv.GPFSVolumeAdapter(
|
||||||
|
self.adpt, 'host_uuid', mock_inst, mock_conn_info)
|
||||||
|
|
||||||
|
def test_get_path(self):
|
||||||
|
self.assertEqual('/gpfs/path', self.vol_drv._get_path())
|
40
nova_powervm/tests/virt/powervm/volume/test_local.py
Normal file
40
nova_powervm/tests/virt/powervm/volume/test_local.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 mock
|
||||||
|
|
||||||
|
from nova_powervm.tests.virt.powervm.volume import test_driver as test_vol
|
||||||
|
from nova_powervm.virt.powervm.volume import local as v_drv
|
||||||
|
|
||||||
|
|
||||||
|
class TestLocalVolumeAdapter(test_vol.TestVolumeAdapter):
|
||||||
|
"""Tests the LocalVolumeAdapter. NovaLink is a I/O host."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestLocalVolumeAdapter, self).setUp()
|
||||||
|
|
||||||
|
# Needed for the volume adapter
|
||||||
|
self.adpt = mock.Mock()
|
||||||
|
mock_inst = mock.MagicMock(uuid='2BC123')
|
||||||
|
|
||||||
|
# Connection Info
|
||||||
|
mock_conn_info = {'data': {'device_path': '/local/path'}}
|
||||||
|
|
||||||
|
self.vol_drv = v_drv.LocalVolumeAdapter(
|
||||||
|
self.adpt, 'host_uuid', mock_inst, mock_conn_info)
|
||||||
|
|
||||||
|
def test_get_path(self):
|
||||||
|
self.assertEqual('/local/path', self.vol_drv._get_path())
|
40
nova_powervm/tests/virt/powervm/volume/test_nfs.py
Normal file
40
nova_powervm/tests/virt/powervm/volume/test_nfs.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 mock
|
||||||
|
|
||||||
|
from nova_powervm.tests.virt.powervm.volume import test_driver as test_vol
|
||||||
|
from nova_powervm.virt.powervm.volume import nfs as v_drv
|
||||||
|
|
||||||
|
|
||||||
|
class TestNFSVolumeAdapter(test_vol.TestVolumeAdapter):
|
||||||
|
"""Tests the NFSVolumeAdapter. NovaLink is a I/O host."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNFSVolumeAdapter, self).setUp()
|
||||||
|
|
||||||
|
# Needed for the volume adapter
|
||||||
|
self.adpt = mock.Mock()
|
||||||
|
mock_inst = mock.MagicMock(uuid='2BC123')
|
||||||
|
|
||||||
|
# Connection Info
|
||||||
|
mock_conn_info = {'data': {'export': '/nfs', 'name': 'path'}}
|
||||||
|
|
||||||
|
self.vol_drv = v_drv.NFSVolumeAdapter(
|
||||||
|
self.adpt, 'host_uuid', mock_inst, mock_conn_info)
|
||||||
|
|
||||||
|
def test_get_path(self):
|
||||||
|
self.assertEqual('/nfs/path', self.vol_drv._get_path())
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2014, 2016 IBM Corp.
|
# Copyright 2014, 2017 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@ -73,16 +73,6 @@ from nova_powervm.virt.powervm import volume as vol_attach
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
# Defines, for all cinder volume types, which volume driver to use. Currently
|
|
||||||
# only supports Fibre Channel, which has multiple options for connections, and
|
|
||||||
# iSCSI.
|
|
||||||
VOLUME_DRIVER_MAPPINGS = {
|
|
||||||
'fibre_channel': vol_attach.FC_STRATEGY_MAPPING[
|
|
||||||
CONF.powervm.fc_attach_strategy.lower()],
|
|
||||||
'iscsi': vol_attach.NETWORK_STRATEGY_MAPPING[
|
|
||||||
CONF.powervm.network_attach_strategy.lower()],
|
|
||||||
}
|
|
||||||
|
|
||||||
DISK_ADPT_NS = 'nova_powervm.virt.powervm.disk'
|
DISK_ADPT_NS = 'nova_powervm.virt.powervm.disk'
|
||||||
DISK_ADPT_MAPPINGS = {
|
DISK_ADPT_MAPPINGS = {
|
||||||
'localdisk': 'localdisk.LocalStorage',
|
'localdisk': 'localdisk.LocalStorage',
|
||||||
@ -752,8 +742,8 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
# Determine if there are volumes to connect. If so, add a connection
|
# Determine if there are volumes to connect. If so, add a connection
|
||||||
# for each type.
|
# for each type.
|
||||||
slot_mgr = slot.build_slot_mgr(instance, self.store_api)
|
slot_mgr = slot.build_slot_mgr(instance, self.store_api)
|
||||||
vol_drv = self._get_inst_vol_adpt(context, instance,
|
vol_drv = vol_attach.build_volume_driver(
|
||||||
conn_info=connection_info)
|
self.adapter, self.host_uuid, instance, connection_info)
|
||||||
flow.add(tf_stg.ConnectVolume(vol_drv, slot_mgr))
|
flow.add(tf_stg.ConnectVolume(vol_drv, slot_mgr))
|
||||||
|
|
||||||
# Save the new slot info
|
# Save the new slot info
|
||||||
@ -776,8 +766,8 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
self._log_operation('detach_volume', instance)
|
self._log_operation('detach_volume', instance)
|
||||||
|
|
||||||
# Get a volume adapter for this volume
|
# Get a volume adapter for this volume
|
||||||
vol_drv = self._get_inst_vol_adpt(ctx.get_admin_context(), instance,
|
vol_drv = vol_attach.build_volume_driver(
|
||||||
conn_info=connection_info)
|
self.adapter, self.host_uuid, instance, connection_info)
|
||||||
|
|
||||||
# Before attempting to detach a volume, ensure the instance exists
|
# Before attempting to detach a volume, ensure the instance exists
|
||||||
# If a live migration fails, the compute manager will call detach
|
# If a live migration fails, the compute manager will call detach
|
||||||
@ -1129,22 +1119,14 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# The host ID
|
# Put the values in the connector
|
||||||
connector = {'host': CONF.host}
|
connector = {}
|
||||||
|
wwpn_list = vol_attach.get_wwpns_for_volume_connector(
|
||||||
# Get the contents from the volume driver
|
self.adapter, self.host_uuid, instance)
|
||||||
vol_drv = self._get_inst_vol_adpt(ctx.get_admin_context(),
|
if wwpn_list is not None:
|
||||||
instance)
|
connector["wwpns"] = wwpn_list
|
||||||
if vol_drv is not None:
|
connector['host'] = vol_attach.get_hostname_for_volume(instance)
|
||||||
|
connector['initiator'] = vol_attach.get_iscsi_initiator(self.adapter)
|
||||||
if CONF.powervm.volume_adapter.lower() == "fibre_channel":
|
|
||||||
# Set the WWPNs
|
|
||||||
wwpn_list = vol_drv.wwpns()
|
|
||||||
if wwpn_list is not None:
|
|
||||||
connector["wwpns"] = wwpn_list
|
|
||||||
connector['host'] = vol_drv.host_name()
|
|
||||||
connector['initiator'] = vol_attach.get_iscsi_initiator(
|
|
||||||
self.adapter)
|
|
||||||
return connector
|
return connector
|
||||||
|
|
||||||
def migrate_disk_and_power_off(self, context, instance, dest,
|
def migrate_disk_and_power_off(self, context, instance, dest,
|
||||||
@ -1720,9 +1702,9 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
if not conn_info:
|
if not conn_info:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
vol_drv = self._get_inst_vol_adpt(context, instance,
|
vol_drv = vol_attach.build_volume_driver(
|
||||||
conn_info=conn_info,
|
self.adapter, self.host_uuid, instance, conn_info,
|
||||||
stg_ftsk=stg_ftsk)
|
stg_ftsk=stg_ftsk)
|
||||||
yield bdm, vol_drv
|
yield bdm, vol_drv
|
||||||
|
|
||||||
def _build_vol_drivers(self, context, instance, block_device_info=None,
|
def _build_vol_drivers(self, context, instance, block_device_info=None,
|
||||||
@ -1836,51 +1818,20 @@ class PowerVMDriver(driver.ComputeDriver):
|
|||||||
# All operations for deploy/destroy require scsi by default. This is
|
# All operations for deploy/destroy require scsi by default. This is
|
||||||
# either vopt, local/SSP disks, etc...
|
# either vopt, local/SSP disks, etc...
|
||||||
xags = {pvm_const.XAG.VIO_SMAP}
|
xags = {pvm_const.XAG.VIO_SMAP}
|
||||||
if not bdms:
|
|
||||||
LOG.debug('Instance XAGs for VM %(inst)s is %(xags)s.',
|
# BDMs could be none, if there are no cinder volumes.
|
||||||
{'inst': instance.name,
|
bdms = bdms if bdms else []
|
||||||
'xags': ','.join(xags)})
|
|
||||||
return list(xags)
|
|
||||||
# If we have any volumes, add the volumes required mapping XAGs.
|
# If we have any volumes, add the volumes required mapping XAGs.
|
||||||
adp_type = VOLUME_DRIVER_MAPPINGS[CONF.powervm.volume_adapter]
|
for bdm in bdms:
|
||||||
vol_cls = importutils.import_class(adp_type)
|
driver_type = bdm.get('connection_info').get('driver_volume_type')
|
||||||
xags.update(set(vol_cls.min_xags()))
|
vol_cls = vol_attach.get_volume_class(driver_type)
|
||||||
|
xags.update(set(vol_cls.min_xags()))
|
||||||
|
|
||||||
LOG.debug('Instance XAGs for VM %(inst)s is %(xags)s.',
|
LOG.debug('Instance XAGs for VM %(inst)s is %(xags)s.',
|
||||||
{'inst': instance.name,
|
{'inst': instance.name, 'xags': ','.join(xags)})
|
||||||
'xags': ','.join(xags)})
|
|
||||||
return list(xags)
|
return list(xags)
|
||||||
|
|
||||||
def _get_inst_vol_adpt(self, context, instance, conn_info=None,
|
|
||||||
stg_ftsk=None):
|
|
||||||
"""Returns the appropriate volume driver based on connection type.
|
|
||||||
|
|
||||||
Checks the connection info for connection-type and return the
|
|
||||||
connector, if no connection info is provided returns the default
|
|
||||||
connector.
|
|
||||||
:param context: security context
|
|
||||||
:param instance: Nova instance for which the volume adapter is needed.
|
|
||||||
:param conn_info: BDM connection information of the instance to
|
|
||||||
get the volume adapter type (vSCSI/NPIV) requested.
|
|
||||||
:param stg_ftsk: (Optional) The FeedTask that can be used to defer the
|
|
||||||
mapping actions against the Virtual I/O Server for. If
|
|
||||||
not provided, then the connect/disconnect actions will
|
|
||||||
be immediate.
|
|
||||||
:return: Returns the volume adapter, if conn_info is not passed then
|
|
||||||
returns the volume adapter based on the CONF
|
|
||||||
fc_attach_strategy property (npiv/vscsi). Otherwise returns
|
|
||||||
the adapter based on the connection-type of
|
|
||||||
connection_info.
|
|
||||||
"""
|
|
||||||
adp_type = VOLUME_DRIVER_MAPPINGS[CONF.powervm.volume_adapter]
|
|
||||||
vol_cls = importutils.import_class(adp_type)
|
|
||||||
if conn_info:
|
|
||||||
LOG.debug('Volume Adapter returned for connection_info=%s',
|
|
||||||
conn_info)
|
|
||||||
LOG.debug('Volume Adapter class %(cls)s for instance %(inst)s',
|
|
||||||
{'cls': vol_cls.__name__, 'inst': instance.name})
|
|
||||||
return vol_cls(self.adapter, self.host_uuid,
|
|
||||||
instance, conn_info, stg_ftsk=stg_ftsk)
|
|
||||||
|
|
||||||
def _get_boot_connectivity_type(self, context, bdms, block_device_info):
|
def _get_boot_connectivity_type(self, context, bdms, block_device_info):
|
||||||
"""Get connectivity information for the instance.
|
"""Get connectivity information for the instance.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2015, 2016 IBM Corp.
|
# Copyright 2015, 2017 IBM Corp.
|
||||||
#
|
#
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
@ -20,7 +20,11 @@ from pypowervm.tasks import partition
|
|||||||
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||||
|
|
||||||
# Defines the various volume connectors that can be used.
|
# Defines the various volume connectors that can be used.
|
||||||
|
from nova import exception
|
||||||
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from nova_powervm import conf as cfg
|
from nova_powervm import conf as cfg
|
||||||
|
from nova_powervm.virt.powervm.i18n import _
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@ -28,10 +32,57 @@ FC_STRATEGY_MAPPING = {
|
|||||||
'npiv': CONF.powervm.fc_npiv_adapter_api,
|
'npiv': CONF.powervm.fc_npiv_adapter_api,
|
||||||
'vscsi': CONF.powervm.fc_vscsi_adapter_api
|
'vscsi': CONF.powervm.fc_vscsi_adapter_api
|
||||||
}
|
}
|
||||||
NETWORK_STRATEGY_MAPPING = {
|
|
||||||
'iscsi': 'nova_powervm.virt.powervm.volume.iscsi.IscsiVolumeAdapter'
|
_STATIC_VOLUME_MAPPINGS = {
|
||||||
|
'iscsi': 'nova_powervm.virt.powervm.volume.iscsi.'
|
||||||
|
'IscsiVolumeAdapter',
|
||||||
|
'local': 'nova_powervm.virt.powervm.volume.local.'
|
||||||
|
'LocalVolumeAdapter',
|
||||||
|
'nfs': 'nova_powervm.virt.powervm.volume.nfs.NFSVolumeAdapter',
|
||||||
|
'gpfs': 'nova_powervm.virt.powervm.volume.gpfs.GPFSVolumeAdapter',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_volume_driver(adapter, host_uuid, instance, conn_info,
|
||||||
|
stg_ftsk=None):
|
||||||
|
vol_cls = get_volume_class(conn_info.get('driver_volume_type'))
|
||||||
|
|
||||||
|
return vol_cls(adapter, host_uuid, instance, conn_info,
|
||||||
|
stg_ftsk=stg_ftsk)
|
||||||
|
|
||||||
|
|
||||||
|
def get_volume_class(drv_type):
|
||||||
|
if drv_type in _STATIC_VOLUME_MAPPINGS:
|
||||||
|
class_type = _STATIC_VOLUME_MAPPINGS[drv_type]
|
||||||
|
elif drv_type == 'fibre_channel':
|
||||||
|
class_type = (FC_STRATEGY_MAPPING[
|
||||||
|
CONF.powervm.fc_attach_strategy.lower()])
|
||||||
|
else:
|
||||||
|
failure_reason = _("Invalid connection type of %s") % drv_type
|
||||||
|
raise exception.InvalidVolume(reason=failure_reason)
|
||||||
|
|
||||||
|
return importutils.import_class(class_type)
|
||||||
|
|
||||||
|
|
||||||
|
def get_hostname_for_volume(instance):
|
||||||
|
if CONF.powervm.fc_attach_strategy.lower() == 'npiv':
|
||||||
|
# Tie the host name to the instance, as it will be represented in
|
||||||
|
# the backend as a full server.
|
||||||
|
host = CONF.host if len(CONF.host) < 20 else CONF.host[:20]
|
||||||
|
return host + '_' + instance.name
|
||||||
|
else:
|
||||||
|
return CONF.host
|
||||||
|
|
||||||
|
|
||||||
|
def get_wwpns_for_volume_connector(adapter, host_uuid, instance):
|
||||||
|
# WWPNs are derived from the FC connector. Pass in a fake connection info
|
||||||
|
# to trick it into thinking it FC
|
||||||
|
fake_fc_conn_info = {'driver_volume_type': 'fibre_channel'}
|
||||||
|
fc_vol_drv = build_volume_driver(adapter, host_uuid, instance,
|
||||||
|
fake_fc_conn_info)
|
||||||
|
return fc_vol_drv.wwpns()
|
||||||
|
|
||||||
|
|
||||||
_ISCSI_INITIATOR = None
|
_ISCSI_INITIATOR = None
|
||||||
_ISCSI_LOOKUP_COMPLETE = False
|
_ISCSI_LOOKUP_COMPLETE = False
|
||||||
|
|
||||||
|
@ -265,10 +265,3 @@ class FibreChannelVolumeAdapter(PowerVMVolumeAdapter):
|
|||||||
:return: The list of WWPNs that need to be included in the zone set.
|
:return: The list of WWPNs that need to be included in the zone set.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def host_name(self):
|
|
||||||
"""Derives the host name that should be used for the storage device.
|
|
||||||
|
|
||||||
:return: The host name.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
97
nova_powervm/virt/powervm/volume/fileio.py
Normal file
97
nova_powervm/virt/powervm/volume/fileio.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# 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 abc
|
||||||
|
import six
|
||||||
|
|
||||||
|
from nova_powervm import conf as cfg
|
||||||
|
from nova_powervm.virt.powervm.i18n import _LI
|
||||||
|
from nova_powervm.virt.powervm.volume import driver as v_driver
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from pypowervm import const as pvm_const
|
||||||
|
from pypowervm.tasks import partition
|
||||||
|
from pypowervm.tasks import scsi_mapper as tsk_map
|
||||||
|
from pypowervm.wrappers import storage as pvm_stg
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class FileIOVolumeAdapter(v_driver.PowerVMVolumeAdapter):
|
||||||
|
"""Base class for connecting file based Cinder Volumes to PowerVM VMs."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def min_xags(cls):
|
||||||
|
return [pvm_const.XAG.VIO_SMAP]
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def _get_path(self):
|
||||||
|
"""Return the path to the file to connect."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _connect_volume(self, slot_mgr):
|
||||||
|
# Get the hosting UUID
|
||||||
|
nl_vios_wrap = partition.get_mgmt_partition(self.adapter)
|
||||||
|
vios_uuid = nl_vios_wrap.uuid
|
||||||
|
|
||||||
|
# Get the File Path
|
||||||
|
fio = pvm_stg.FileIO.bld(self.adapter, self._get_path())
|
||||||
|
|
||||||
|
def add_func(vios_w):
|
||||||
|
# If the vios doesn't match, just return
|
||||||
|
if vios_w.uuid != vios_uuid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
LOG.info(_LI("Adding logical volume disk connection between VM "
|
||||||
|
"%(vm)s and VIOS %(vios)s."),
|
||||||
|
{'vm': self.instance.name, 'vios': vios_w.name},
|
||||||
|
instance=self.instance)
|
||||||
|
mapping = tsk_map.build_vscsi_mapping(
|
||||||
|
self.host_uuid, vios_w, self.vm_uuid, fio)
|
||||||
|
return tsk_map.add_map(vios_w, mapping)
|
||||||
|
|
||||||
|
self.stg_ftsk.add_functor_subtask(add_func)
|
||||||
|
|
||||||
|
def _disconnect_volume(self, slot_mgr):
|
||||||
|
# Get the hosting UUID
|
||||||
|
nl_vios_wrap = partition.get_mgmt_partition(self.adapter)
|
||||||
|
vios_uuid = nl_vios_wrap.uuid
|
||||||
|
|
||||||
|
# Build the match function
|
||||||
|
match_func = tsk_map.gen_match_func(pvm_stg.FileIO,
|
||||||
|
names=[self._get_path()])
|
||||||
|
|
||||||
|
# Make sure the remove function will run within the transaction manager
|
||||||
|
def rm_func(vios_w):
|
||||||
|
# If the vios doesn't match, just return
|
||||||
|
if vios_w.uuid != vios_uuid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
LOG.info(_LI("Disconnecting instance %(inst)s from storage "
|
||||||
|
"disks."), {'inst': self.instance.name},
|
||||||
|
instance=self.instance)
|
||||||
|
return tsk_map.remove_maps(vios_w, self.vm_uuid,
|
||||||
|
match_func=match_func)
|
||||||
|
|
||||||
|
self.stg_ftsk.add_functor_subtask(rm_func)
|
||||||
|
|
||||||
|
# Find the disk directly.
|
||||||
|
mappings = tsk_map.find_maps(nl_vios_wrap.scsi_mappings,
|
||||||
|
client_lpar_id=self.vm_uuid,
|
||||||
|
match_func=match_func)
|
||||||
|
|
||||||
|
return [x.backing_storage for x in mappings]
|
24
nova_powervm/virt/powervm/volume/gpfs.py
Normal file
24
nova_powervm/virt/powervm/volume/gpfs.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nova_powervm.virt.powervm.volume import fileio
|
||||||
|
|
||||||
|
|
||||||
|
class GPFSVolumeAdapter(fileio.FileIOVolumeAdapter):
|
||||||
|
"""Connects GPFS Cinder Volumes to PowerVM VMs."""
|
||||||
|
|
||||||
|
def _get_path(self):
|
||||||
|
return self.connection_info.get("data")['device_path']
|
@ -56,9 +56,6 @@ class IscsiVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||||||
"""The type of volume supported by this type."""
|
"""The type of volume supported by this type."""
|
||||||
return 'iscsi'
|
return 'iscsi'
|
||||||
|
|
||||||
def host_name(self):
|
|
||||||
return CONF.host
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def min_xags(cls):
|
def min_xags(cls):
|
||||||
"""List of pypowervm XAGs needed to support this adapter."""
|
"""List of pypowervm XAGs needed to support this adapter."""
|
||||||
|
24
nova_powervm/virt/powervm/volume/local.py
Normal file
24
nova_powervm/virt/powervm/volume/local.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nova_powervm.virt.powervm.volume import fileio
|
||||||
|
|
||||||
|
|
||||||
|
class LocalVolumeAdapter(fileio.FileIOVolumeAdapter):
|
||||||
|
"""Connects Local Cinder Volumes to PowerVM VMs."""
|
||||||
|
|
||||||
|
def _get_path(self):
|
||||||
|
return self.connection_info['data']['device_path']
|
26
nova_powervm/virt/powervm/volume/nfs.py
Normal file
26
nova_powervm/virt/powervm/volume/nfs.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2017 IBM Corp.
|
||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from nova_powervm.virt.powervm.volume import fileio
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class NFSVolumeAdapter(fileio.FileIOVolumeAdapter):
|
||||||
|
"""Connects NFS Cinder Volumes to PowerVM VMs."""
|
||||||
|
|
||||||
|
def _get_path(self):
|
||||||
|
return os.path.join(self.connection_info['data']['export'],
|
||||||
|
self.connection_info['data']['name'])
|
@ -592,14 +592,6 @@ class NPIVVolumeAdapter(v_driver.FibreChannelVolumeAdapter):
|
|||||||
"%(fabric)s."), {'fabric': fabric},
|
"%(fabric)s."), {'fabric': fabric},
|
||||||
instance=self.instance)
|
instance=self.instance)
|
||||||
|
|
||||||
def host_name(self):
|
|
||||||
"""Derives the host name that should be used for the storage device.
|
|
||||||
|
|
||||||
:return: The host name.
|
|
||||||
"""
|
|
||||||
host = CONF.host if len(CONF.host) < 20 else CONF.host[:20]
|
|
||||||
return host + '_' + self.instance.name
|
|
||||||
|
|
||||||
def _set_fabric_state(self, fabric, state):
|
def _set_fabric_state(self, fabric, state):
|
||||||
"""Sets the fabric state into the instance's system metadata.
|
"""Sets the fabric state into the instance's system metadata.
|
||||||
|
|
||||||
|
@ -355,13 +355,6 @@ class PVVscsiFCVolumeAdapter(volume.VscsiVolumeAdapter,
|
|||||||
_vscsi_pfc_wwpns = pvm_tpar.get_physical_wwpns(self.adapter)
|
_vscsi_pfc_wwpns = pvm_tpar.get_physical_wwpns(self.adapter)
|
||||||
return _vscsi_pfc_wwpns
|
return _vscsi_pfc_wwpns
|
||||||
|
|
||||||
def host_name(self):
|
|
||||||
"""Derives the host name that should be used for the storage device.
|
|
||||||
|
|
||||||
:return: The host name.
|
|
||||||
"""
|
|
||||||
return CONF.host
|
|
||||||
|
|
||||||
def _get_hdisk_itls(self, vios_w):
|
def _get_hdisk_itls(self, vios_w):
|
||||||
"""Returns the mapped ITLs for the hdisk for the given VIOS.
|
"""Returns the mapped ITLs for the hdisk for the given VIOS.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user