From 937aa55ffa0deef007046bec0a0c42a3d9d8c895 Mon Sep 17 00:00:00 2001 From: Lucian Petrut Date: Fri, 16 Feb 2018 15:18:46 +0200 Subject: [PATCH] Cluster driver: Blacklist iSCSI/FC volume drivers There are critical known issues related to the cluster driver and passthrough disks. When an instance is failed over, the instance attached volumes need to be accessible on the destination host. This will not happen transparently for such disks, for which reason the instance may bounce between a few nodes and then end up in error state, basically defeating the purpose of the cluster driver. This change ensures that the Hyper-V Cluster driver rejects iSCSI/FC Cinder volumes, raising an error that describes possible alternatives. Closes-Bug: #1749958 Change-Id: I231b51c648e1349fcb8df4cd818b2d2887a55d69 --- compute_hyperv/nova/cluster/driver.py | 2 + compute_hyperv/nova/cluster/volumeops.py | 49 ++++++++++++++++++ compute_hyperv/nova/volumeops.py | 4 ++ .../tests/unit/cluster/test_volumeops.py | 50 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 compute_hyperv/nova/cluster/volumeops.py create mode 100644 compute_hyperv/tests/unit/cluster/test_volumeops.py diff --git a/compute_hyperv/nova/cluster/driver.py b/compute_hyperv/nova/cluster/driver.py index a280581b..5e44ffbf 100644 --- a/compute_hyperv/nova/cluster/driver.py +++ b/compute_hyperv/nova/cluster/driver.py @@ -16,6 +16,7 @@ from compute_hyperv.nova.cluster import clusterops from compute_hyperv.nova.cluster import livemigrationops +from compute_hyperv.nova.cluster import volumeops from compute_hyperv.nova import driver @@ -25,6 +26,7 @@ class HyperVClusterDriver(driver.HyperVDriver): self._clops = clusterops.ClusterOps() self._livemigrationops = livemigrationops.ClusterLiveMigrationOps() + self._volumeops = volumeops.ClusterVolumeOps() self._clops.start_failover_listener_daemon() self._clops.reclaim_failovered_instances() diff --git a/compute_hyperv/nova/cluster/volumeops.py b/compute_hyperv/nova/cluster/volumeops.py new file mode 100644 index 00000000..d6dd450b --- /dev/null +++ b/compute_hyperv/nova/cluster/volumeops.py @@ -0,0 +1,49 @@ +# Copyright 2018 Cloudbase Solutions Srl +# +# 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 import exception +from oslo_log import log as logging + +from compute_hyperv.nova import constants +from compute_hyperv.nova import volumeops + +LOG = logging.getLogger(__name__) + + +class ClusterVolumeOps(volumeops.VolumeOps): + def _load_volume_drivers(self): + self.volume_drivers = { + constants.STORAGE_PROTOCOL_SMBFS: volumeops.SMBFSVolumeDriver() + } + + def _get_volume_driver(self, connection_info): + driver_type = connection_info.get('driver_volume_type') + if driver_type in [constants.STORAGE_PROTOCOL_ISCSI, + constants.STORAGE_PROTOCOL_FC]: + err_msg = ( + "The Hyper-V Cluster driver does not currently support " + "passthrough disks (e.g. iSCSI/FC disks). The reason is " + "that the volumes need to be available on the destination " + "host side during an unexpected instance failover. In order " + "to leverage your storage backend, you may either use the " + "*standard* Nova Hyper-V driver or use the Cinder SMB volume " + "driver (which may imply deploying CSVs on top of LUNs " + "exposed by your storage backend).") + LOG.error(err_msg) + raise exception.VolumeDriverNotFound(driver_type=driver_type) + + return super(ClusterVolumeOps, self)._get_volume_driver( + connection_info) diff --git a/compute_hyperv/nova/volumeops.py b/compute_hyperv/nova/volumeops.py index cba02618..ebe27c4e 100644 --- a/compute_hyperv/nova/volumeops.py +++ b/compute_hyperv/nova/volumeops.py @@ -77,6 +77,10 @@ class VolumeOps(object): self._vmutils = utilsfactory.get_vmutils() self._default_root_device = 'vda' + + self._load_volume_drivers() + + def _load_volume_drivers(self): self.volume_drivers = { constants.STORAGE_PROTOCOL_SMBFS: SMBFSVolumeDriver(), constants.STORAGE_PROTOCOL_ISCSI: ISCSIVolumeDriver(), diff --git a/compute_hyperv/tests/unit/cluster/test_volumeops.py b/compute_hyperv/tests/unit/cluster/test_volumeops.py new file mode 100644 index 00000000..5db4e10f --- /dev/null +++ b/compute_hyperv/tests/unit/cluster/test_volumeops.py @@ -0,0 +1,50 @@ +# Copyright 2018 Cloudbase Solutions Srl +# +# 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 import exception + +from compute_hyperv.nova.cluster import volumeops +from compute_hyperv.nova import constants +from compute_hyperv.nova import volumeops as base_volumeops +from compute_hyperv.tests.unit import test_base + + +class ClusterVolumeOpsTestCase(test_base.HyperVBaseTestCase): + _autospec_classes = [ + base_volumeops.cinder.API, + ] + + def setUp(self): + super(ClusterVolumeOpsTestCase, self).setUp() + self._volumeops = volumeops.ClusterVolumeOps() + + def test_loaded_volume_drivers(self): + self.assertEqual(set([constants.STORAGE_PROTOCOL_SMBFS]), + set(self._volumeops.volume_drivers.keys())) + + def test_get_blacklisted_volume_driver(self): + conn_info = dict(driver_volume_type=constants.STORAGE_PROTOCOL_ISCSI) + + self.assertRaises( + exception.VolumeDriverNotFound, + self._volumeops._get_volume_driver, + conn_info) + + def test_get_supported_volume_driver(self): + conn_info = dict(driver_volume_type=constants.STORAGE_PROTOCOL_SMBFS) + drv = self._volumeops._get_volume_driver(conn_info) + + self.assertIsInstance(drv, base_volumeops.SMBFSVolumeDriver)