Provide minimum VIOS connection on vSCSI volumes
When the admin attempts to connect a volume, they may wish to specify a minimum level of redundancy (how many Virtual I/O Servers it connects to). This change set provides the operator with the ability to set a minimum number of VIOSes. Change-Id: Id4d4ca945190f04436792cac12fa08f4412bc114 Closes-Bug: 1504037
This commit is contained in:
parent
f7de66aac0
commit
4ce1db1143
@ -118,6 +118,10 @@ Volume Options
|
||||
| | Cinder volumes should be attached to the Virtual Machine. |
|
||||
| | The options are: npiv or vscsi. |
|
||||
+--------------------------------------+------------------------------------------------------------+
|
||||
| vscsi_vios_connections_required = 1 | (IntOpt) Indicates a minimum number of Virtual I/O Servers |
|
||||
| | that are required to support a Cinder volume attach with |
|
||||
| | the vSCSI volume connector. |
|
||||
+--------------------------------------+------------------------------------------------------------+
|
||||
| ports_per_fabric = 1 | (IntOpt) (NPIV only) The number of physical ports that |
|
||||
| | should be connected directly to the Virtual Machine, per |
|
||||
| | fabric. |
|
||||
|
@ -146,8 +146,11 @@ class TestVSCSIAdapter(BaseVSCSITest):
|
||||
@mock.patch('pypowervm.tasks.scsi_mapper.add_map')
|
||||
@mock.patch('pypowervm.tasks.scsi_mapper.build_vscsi_mapping')
|
||||
@mock.patch('pypowervm.tasks.hdisk.discover_hdisk')
|
||||
def test_connect_volume_no_update(self, mock_disc_hdisk, mock_build_map,
|
||||
mock_add_map):
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.vscsi.VscsiVolumeAdapter.'
|
||||
'_validate_vios_on_connection')
|
||||
def test_connect_volume_no_update(
|
||||
self, mock_validate_vioses, mock_disc_hdisk, mock_build_map,
|
||||
mock_add_map):
|
||||
"""Make sure we don't do an actual update of the VIOS if not needed."""
|
||||
# The mock return values
|
||||
mock_build_map.return_value = 'fake_map'
|
||||
@ -159,6 +162,7 @@ class TestVSCSIAdapter(BaseVSCSITest):
|
||||
self.vol_drv.connect_volume()
|
||||
|
||||
# As initialized above, remove_maps returns True to trigger update.
|
||||
mock_validate_vioses.assert_called_with(1)
|
||||
self.assertEqual(1, mock_add_map.call_count)
|
||||
self.assertEqual(0, self.ft_fx.patchers['update'].mock.call_count)
|
||||
self.assertEqual(1, mock_disc_hdisk.call_count)
|
||||
@ -166,8 +170,11 @@ class TestVSCSIAdapter(BaseVSCSITest):
|
||||
@mock.patch('pypowervm.tasks.hdisk.build_itls')
|
||||
@mock.patch('pypowervm.tasks.hdisk.lua_recovery')
|
||||
@mock.patch('pypowervm.tasks.scsi_mapper.add_vscsi_mapping')
|
||||
def test_connect_volume_to_initiatiors(self, mock_add_vscsi_mapping,
|
||||
mock_lua_recovery, mock_build_itls):
|
||||
@mock.patch('nova_powervm.virt.powervm.volume.vscsi.VscsiVolumeAdapter.'
|
||||
'_validate_vios_on_connection')
|
||||
def test_connect_volume_to_initiatiors(
|
||||
self, mock_validate_vioses, mock_add_vscsi_mapping, mock_lua_recovery,
|
||||
mock_build_itls):
|
||||
"""Tests that the connect w/out initiators throws errors."""
|
||||
mock_lua_recovery.return_value = (
|
||||
hdisk.LUAStatus.DEVICE_AVAILABLE, 'devname', 'udid')
|
||||
@ -175,10 +182,32 @@ class TestVSCSIAdapter(BaseVSCSITest):
|
||||
mock_instance = mock.Mock()
|
||||
mock_instance.system_metadata = {}
|
||||
|
||||
mock_validate_vioses.side_effect = p_exc.VolumeAttachFailed(
|
||||
volume_id='1', reason='message', instance_name='inst')
|
||||
|
||||
mock_build_itls.return_value = []
|
||||
self.assertRaises(p_exc.VolumeAttachFailed,
|
||||
self.vol_drv.connect_volume)
|
||||
|
||||
# Validate that the validate was called with no vioses.
|
||||
mock_validate_vioses.assert_called_with(0)
|
||||
|
||||
def test_validate_vios_on_connection(self):
|
||||
# Happy path!
|
||||
self.vol_drv._validate_vios_on_connection(1)
|
||||
|
||||
# Raise if no VIOSes are found
|
||||
self.assertRaises(p_exc.VolumeAttachFailed,
|
||||
self.vol_drv._validate_vios_on_connection, 0)
|
||||
|
||||
# Multi VIOS required happy path.
|
||||
self.flags(vscsi_vios_connections_required=2, group='powervm')
|
||||
self.vol_drv._validate_vios_on_connection(2)
|
||||
|
||||
# Raise if multiple VIOSes required
|
||||
self.assertRaises(p_exc.VolumeAttachFailed,
|
||||
self.vol_drv._validate_vios_on_connection, 1)
|
||||
|
||||
@mock.patch('pypowervm.tasks.hdisk.remove_hdisk')
|
||||
@mock.patch('pypowervm.wrappers.virtual_io_server.VIOS.hdisk_from_uuid')
|
||||
@mock.patch('pypowervm.tasks.scsi_mapper.remove_maps')
|
||||
|
@ -34,7 +34,11 @@ vol_adapter_opts = [
|
||||
default='nova_powervm.virt.powervm.volume.vscsi.'
|
||||
'VscsiVolumeAdapter',
|
||||
help='Volume Adapter API to connect FC volumes through Virtual '
|
||||
'I/O Server using PowerVM vSCSI connection mechanism')
|
||||
'I/O Server using PowerVM vSCSI connection mechanism'),
|
||||
cfg.IntOpt('vscsi_vios_connections_required', default=1,
|
||||
help='Indicates a minimum number of Virtual I/O Servers that '
|
||||
'are required to support a Cinder volume attach with the '
|
||||
'vSCSI volume connector.')
|
||||
]
|
||||
CONF.register_opts(vol_adapter_opts, group='powervm')
|
||||
|
||||
|
@ -200,16 +200,44 @@ class VscsiVolumeAdapter(v_driver.FibreChannelVolumeAdapter):
|
||||
connect_volume_to_vio, provides='vio_modified', flag_update=False)
|
||||
ret = connect_ftsk.execute()
|
||||
|
||||
# If no valid hdisk was found, log and exit
|
||||
if not any([result['vio_modified']
|
||||
for result in ret['wrapper_task_rets'].values()]):
|
||||
# Check the number of VIOSes
|
||||
vioses_modified = 0
|
||||
for result in ret['wrapper_task_rets'].values():
|
||||
if result['vio_modified']:
|
||||
vioses_modified += 1
|
||||
self._validate_vios_on_connection(vioses_modified)
|
||||
|
||||
def _validate_vios_on_connection(self, num_vioses_found):
|
||||
"""Validates that the correct number of VIOSes were discovered.
|
||||
|
||||
Certain environments may have redundancy requirements. For PowerVM
|
||||
this is achieved by having multiple Virtual I/O Servers. This method
|
||||
will check to ensure that the operator's requirements for redundancy
|
||||
have been met. If not, a specific error message will be raised.
|
||||
|
||||
:param num_vioses_found: The number of VIOSes the hdisk was found on.
|
||||
"""
|
||||
# Is valid as long as the vios count exceeds the conf value.
|
||||
if num_vioses_found >= CONF.powervm.vscsi_vios_connections_required:
|
||||
return
|
||||
|
||||
# Should have a custom message based on zero or 'some but not enough'
|
||||
# I/O Servers.
|
||||
if num_vioses_found == 0:
|
||||
msg = (_('Failed to discover valid hdisk on any Virtual I/O '
|
||||
'Server for volume %(volume_id)s.') %
|
||||
{'volume_id': self.volume_id})
|
||||
LOG.error(msg)
|
||||
ex_args = {'volume_id': self.volume_id, 'reason': msg,
|
||||
'instance_name': self.instance.name}
|
||||
raise p_exc.VolumeAttachFailed(**ex_args)
|
||||
else:
|
||||
msg = (_('Failed to discover the hdisk on the required number of '
|
||||
'Virtual I/O Servers. Volume %(volume_id)s required '
|
||||
'%(vios_req)d Virtual I/O Servers, but the disk was only '
|
||||
'found on %(vios_act)d Virtual I/O Servers.') %
|
||||
{'volume_id': self.volume_id, 'vios_act': num_vioses_found,
|
||||
'vios_req': CONF.powervm.vscsi_vios_connections_required})
|
||||
LOG.error(msg)
|
||||
ex_args = {'volume_id': self.volume_id, 'reason': msg,
|
||||
'instance_name': self.instance.name}
|
||||
raise p_exc.VolumeAttachFailed(**ex_args)
|
||||
|
||||
def _disconnect_volume(self):
|
||||
"""Disconnect the volume."""
|
||||
|
Loading…
Reference in New Issue
Block a user