Remove the HGST connector
The HGST driver was removed from Cinder in the Stein release. This cleans up its connector code. Depends-on: https://review.opendev.org/745393 Change-Id: I9cb2911cba9f8f3310d6132bb8f96b45bf5b0708 Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
This commit is contained in:
@@ -50,7 +50,6 @@ SMBFS = 'SMBFS'
|
|||||||
GLUSTERFS = "GLUSTERFS"
|
GLUSTERFS = "GLUSTERFS"
|
||||||
LOCAL = "LOCAL"
|
LOCAL = "LOCAL"
|
||||||
HUAWEISDSHYPERVISOR = "HUAWEISDSHYPERVISOR"
|
HUAWEISDSHYPERVISOR = "HUAWEISDSHYPERVISOR"
|
||||||
HGST = "HGST"
|
|
||||||
RBD = "RBD"
|
RBD = "RBD"
|
||||||
SCALEIO = "SCALEIO"
|
SCALEIO = "SCALEIO"
|
||||||
SCALITY = "SCALITY"
|
SCALITY = "SCALITY"
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ unix_connector_list = [
|
|||||||
'os_brick.initiator.connectors.gpfs.GPFSConnector',
|
'os_brick.initiator.connectors.gpfs.GPFSConnector',
|
||||||
'os_brick.initiator.connectors.drbd.DRBDConnector',
|
'os_brick.initiator.connectors.drbd.DRBDConnector',
|
||||||
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
|
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
|
||||||
'os_brick.initiator.connectors.hgst.HGSTConnector',
|
|
||||||
'os_brick.initiator.connectors.scaleio.ScaleIOConnector',
|
'os_brick.initiator.connectors.scaleio.ScaleIOConnector',
|
||||||
'os_brick.initiator.connectors.disco.DISCOConnector',
|
'os_brick.initiator.connectors.disco.DISCOConnector',
|
||||||
'os_brick.initiator.connectors.vmware.VmdkConnector',
|
'os_brick.initiator.connectors.vmware.VmdkConnector',
|
||||||
@@ -107,8 +106,6 @@ _connector_mapping_linux = {
|
|||||||
'os_brick.initiator.connectors.local.LocalConnector',
|
'os_brick.initiator.connectors.local.LocalConnector',
|
||||||
initiator.HUAWEISDSHYPERVISOR:
|
initiator.HUAWEISDSHYPERVISOR:
|
||||||
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
|
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
|
||||||
initiator.HGST:
|
|
||||||
'os_brick.initiator.connectors.hgst.HGSTConnector',
|
|
||||||
initiator.RBD:
|
initiator.RBD:
|
||||||
'os_brick.initiator.connectors.rbd.RBDConnector',
|
'os_brick.initiator.connectors.rbd.RBDConnector',
|
||||||
initiator.SCALEIO:
|
initiator.SCALEIO:
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
# 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 os
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils as putils
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from os_brick import exception
|
|
||||||
from os_brick.i18n import _
|
|
||||||
from os_brick import initiator
|
|
||||||
from os_brick.initiator.connectors import base
|
|
||||||
from os_brick import utils
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class HGSTConnector(base.BaseLinuxConnector):
|
|
||||||
"""Connector class to attach/detach HGST volumes."""
|
|
||||||
|
|
||||||
VGCCLUSTER = 'vgc-cluster'
|
|
||||||
|
|
||||||
def __init__(self, root_helper, driver=None,
|
|
||||||
device_scan_attempts=initiator.DEVICE_SCAN_ATTEMPTS_DEFAULT,
|
|
||||||
*args, **kwargs):
|
|
||||||
super(HGSTConnector, self).__init__(root_helper, driver=driver,
|
|
||||||
device_scan_attempts=
|
|
||||||
device_scan_attempts,
|
|
||||||
*args, **kwargs)
|
|
||||||
self._vgc_host = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_connector_properties(root_helper, *args, **kwargs):
|
|
||||||
"""The HGST connector properties."""
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def _log_cli_err(self, err):
|
|
||||||
"""Dumps the full command output to a logfile in error cases."""
|
|
||||||
LOG.error("CLI fail: '%(cmd)s' = %(code)s\nout: %(stdout)s\n"
|
|
||||||
"err: %(stderr)s",
|
|
||||||
{'cmd': err.cmd, 'code': err.exit_code,
|
|
||||||
'stdout': err.stdout, 'stderr': err.stderr})
|
|
||||||
|
|
||||||
def _find_vgc_host(self):
|
|
||||||
"""Finds vgc-cluster hostname for this box."""
|
|
||||||
params = [self.VGCCLUSTER, "domain-list", "-1"]
|
|
||||||
try:
|
|
||||||
out, unused = self._execute(*params, run_as_root=True,
|
|
||||||
root_helper=self._root_helper)
|
|
||||||
except putils.ProcessExecutionError as err:
|
|
||||||
self._log_cli_err(err)
|
|
||||||
msg = _("Unable to get list of domain members, check that "
|
|
||||||
"the cluster is running.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
domain = out.splitlines()
|
|
||||||
params = ["ip", "addr", "list"]
|
|
||||||
try:
|
|
||||||
out, unused = self._execute(*params, run_as_root=False)
|
|
||||||
except putils.ProcessExecutionError as err:
|
|
||||||
self._log_cli_err(err)
|
|
||||||
msg = _("Unable to get list of IP addresses on this host, "
|
|
||||||
"check permissions and networking.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
nets = out.splitlines()
|
|
||||||
for host in domain:
|
|
||||||
try:
|
|
||||||
ip = socket.gethostbyname(host)
|
|
||||||
for line in nets:
|
|
||||||
x = line.strip()
|
|
||||||
if x.startswith("inet %s/" % ip):
|
|
||||||
return host
|
|
||||||
except socket.error:
|
|
||||||
pass
|
|
||||||
msg = _("Current host isn't part of HGST domain.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
|
|
||||||
def _hostname(self):
|
|
||||||
"""Returns hostname to use for cluster operations on this box."""
|
|
||||||
if self._vgc_host is None:
|
|
||||||
self._vgc_host = self._find_vgc_host()
|
|
||||||
return self._vgc_host
|
|
||||||
|
|
||||||
def get_search_path(self):
|
|
||||||
return "/dev"
|
|
||||||
|
|
||||||
def get_volume_paths(self, connection_properties):
|
|
||||||
path = ("%(path)s/%(name)s" %
|
|
||||||
{'path': self.get_search_path(),
|
|
||||||
'name': connection_properties['name']})
|
|
||||||
volume_path = None
|
|
||||||
if os.path.exists(path):
|
|
||||||
volume_path = path
|
|
||||||
return [volume_path]
|
|
||||||
|
|
||||||
@utils.trace
|
|
||||||
def connect_volume(self, connection_properties):
|
|
||||||
"""Attach a Space volume to running host.
|
|
||||||
|
|
||||||
:param connection_properties: The dictionary that describes all
|
|
||||||
of the target volume attributes.
|
|
||||||
connection_properties for HGST must include:
|
|
||||||
name - Name of space to attach
|
|
||||||
:type connection_properties: dict
|
|
||||||
:returns: dict
|
|
||||||
"""
|
|
||||||
if connection_properties is None:
|
|
||||||
msg = _("Connection properties passed in as None.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
if 'name' not in connection_properties:
|
|
||||||
msg = _("Connection properties missing 'name' field.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
device_info = {
|
|
||||||
'type': 'block',
|
|
||||||
'device': connection_properties['name'],
|
|
||||||
'path': '/dev/' + connection_properties['name']
|
|
||||||
}
|
|
||||||
volname = device_info['device']
|
|
||||||
params = [self.VGCCLUSTER, 'space-set-apphosts']
|
|
||||||
params += ['-n', volname]
|
|
||||||
params += ['-A', self._hostname()]
|
|
||||||
params += ['--action', 'ADD']
|
|
||||||
try:
|
|
||||||
self._execute(*params, run_as_root=True,
|
|
||||||
root_helper=self._root_helper)
|
|
||||||
except putils.ProcessExecutionError as err:
|
|
||||||
self._log_cli_err(err)
|
|
||||||
msg = (_("Unable to set apphost for space %s") % volname)
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
|
|
||||||
return device_info
|
|
||||||
|
|
||||||
@utils.trace
|
|
||||||
def disconnect_volume(self, connection_properties, device_info,
|
|
||||||
force=False, ignore_errors=False):
|
|
||||||
"""Detach and flush the volume.
|
|
||||||
|
|
||||||
:param connection_properties: The dictionary that describes all
|
|
||||||
of the target volume attributes.
|
|
||||||
For HGST must include:
|
|
||||||
name - Name of space to detach
|
|
||||||
noremovehost - Host which should never be removed
|
|
||||||
:type connection_properties: dict
|
|
||||||
:param device_info: historical difference, but same as connection_props
|
|
||||||
:type device_info: dict
|
|
||||||
"""
|
|
||||||
if connection_properties is None:
|
|
||||||
msg = _("Connection properties passed in as None.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
if 'name' not in connection_properties:
|
|
||||||
msg = _("Connection properties missing 'name' field.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
if 'noremovehost' not in connection_properties:
|
|
||||||
msg = _("Connection properties missing 'noremovehost' field.")
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
if connection_properties['noremovehost'] != self._hostname():
|
|
||||||
params = [self.VGCCLUSTER, 'space-set-apphosts']
|
|
||||||
params += ['-n', connection_properties['name']]
|
|
||||||
params += ['-A', self._hostname()]
|
|
||||||
params += ['--action', 'DELETE']
|
|
||||||
try:
|
|
||||||
self._execute(*params, run_as_root=True,
|
|
||||||
root_helper=self._root_helper)
|
|
||||||
except putils.ProcessExecutionError as err:
|
|
||||||
self._log_cli_err(err)
|
|
||||||
msg = (_("Unable to set apphost for space %s") %
|
|
||||||
connection_properties['name'])
|
|
||||||
raise exception.BrickException(message=msg)
|
|
||||||
|
|
||||||
def extend_volume(self, connection_properties):
|
|
||||||
# TODO(walter-boring): is this possible?
|
|
||||||
raise NotImplementedError
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils as putils
|
|
||||||
|
|
||||||
from os_brick import exception
|
|
||||||
from os_brick.initiator import connector
|
|
||||||
from os_brick.initiator.connectors import hgst
|
|
||||||
from os_brick.tests.initiator import test_connector
|
|
||||||
|
|
||||||
|
|
||||||
class HGSTConnectorTestCase(test_connector.ConnectorTestCase):
|
|
||||||
"""Test cases for HGST initiator class."""
|
|
||||||
|
|
||||||
IP_OUTPUT = """
|
|
||||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
|
|
||||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
|
||||||
inet 127.0.0.1/8 scope host lo
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
inet 169.254.169.254/32 scope link lo
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
inet6 ::1/128 scope host
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
2: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master
|
|
||||||
link/ether 00:25:90:d9:18:08 brd ff:ff:ff:ff:ff:ff
|
|
||||||
inet6 fe80::225:90ff:fed9:1808/64 scope link
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
3: em2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state
|
|
||||||
link/ether 00:25:90:d9:18:09 brd ff:ff:ff:ff:ff:ff
|
|
||||||
inet 192.168.0.23/24 brd 192.168.0.255 scope global em2
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
inet6 fe80::225:90ff:fed9:1809/64 scope link
|
|
||||||
valid_lft forever preferred_lft forever
|
|
||||||
"""
|
|
||||||
|
|
||||||
DOMAIN_OUTPUT = """localhost"""
|
|
||||||
|
|
||||||
DOMAIN_FAILED = """this.better.not.resolve.to.a.name.or.else"""
|
|
||||||
|
|
||||||
SET_APPHOST_OUTPUT = """
|
|
||||||
VLVM_SET_APPHOSTS0000000395
|
|
||||||
Request Succeeded
|
|
||||||
"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(HGSTConnectorTestCase, self).setUp()
|
|
||||||
self.connector = hgst.HGSTConnector(
|
|
||||||
None, execute=self._fake_exec)
|
|
||||||
self._fail_set_apphosts = False
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
|
|
||||||
def _fake_exec_set_apphosts(self, *cmd):
|
|
||||||
if self._fail_set_apphosts:
|
|
||||||
raise putils.ProcessExecutionError(None, None, 1)
|
|
||||||
else:
|
|
||||||
return self.SET_APPHOST_OUTPUT, ''
|
|
||||||
|
|
||||||
def _fake_exec_ip(self, *cmd):
|
|
||||||
if self._fail_ip:
|
|
||||||
# Remove localhost so there is no IP match
|
|
||||||
return self.IP_OUTPUT.replace("127.0.0.1", "x.x.x.x"), ''
|
|
||||||
else:
|
|
||||||
return self.IP_OUTPUT, ''
|
|
||||||
|
|
||||||
def _fake_exec_domain_list(self, *cmd):
|
|
||||||
if self._fail_domain_list:
|
|
||||||
return self.DOMAIN_FAILED, ''
|
|
||||||
else:
|
|
||||||
return self.DOMAIN_OUTPUT, ''
|
|
||||||
|
|
||||||
def _fake_exec(self, *cmd, **kwargs):
|
|
||||||
self.cmdline = " ".join(cmd)
|
|
||||||
if cmd[0] == "ip":
|
|
||||||
return self._fake_exec_ip(*cmd)
|
|
||||||
elif cmd[0] == "vgc-cluster":
|
|
||||||
if cmd[1] == "domain-list":
|
|
||||||
return self._fake_exec_domain_list(*cmd)
|
|
||||||
elif cmd[1] == "space-set-apphosts":
|
|
||||||
return self._fake_exec_set_apphosts(*cmd)
|
|
||||||
else:
|
|
||||||
return '', ''
|
|
||||||
|
|
||||||
def test_factory(self):
|
|
||||||
"""Can we instantiate a HGSTConnector of the right kind?"""
|
|
||||||
obj = connector.InitiatorConnector.factory('HGST', None, arch='x86_64')
|
|
||||||
self.assertEqual("HGSTConnector", obj.__class__.__name__)
|
|
||||||
|
|
||||||
def test_get_search_path(self):
|
|
||||||
expected = "/dev"
|
|
||||||
actual = self.connector.get_search_path()
|
|
||||||
self.assertEqual(expected, actual)
|
|
||||||
|
|
||||||
@mock.patch.object(os.path, 'exists', return_value=True)
|
|
||||||
def test_get_volume_paths(self, mock_exists):
|
|
||||||
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
path = "/dev/%s" % cprops['name']
|
|
||||||
expected = [path]
|
|
||||||
actual = self.connector.get_volume_paths(cprops)
|
|
||||||
self.assertEqual(expected, actual)
|
|
||||||
|
|
||||||
def test_connect_volume(self):
|
|
||||||
"""Tests that a simple connection succeeds"""
|
|
||||||
self._fail_set_apphosts = False
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
dev_info = self.connector.connect_volume(cprops)
|
|
||||||
self.assertEqual('block', dev_info['type'])
|
|
||||||
self.assertEqual('space', dev_info['device'])
|
|
||||||
self.assertEqual('/dev/space', dev_info['path'])
|
|
||||||
|
|
||||||
def test_get_connector_properties(self):
|
|
||||||
props = hgst.HGSTConnector.get_connector_properties(
|
|
||||||
'sudo', multipath=True, enforce_multipath=True)
|
|
||||||
|
|
||||||
expected_props = {}
|
|
||||||
self.assertEqual(expected_props, props)
|
|
||||||
|
|
||||||
def test_connect_volume_nohost_fail(self):
|
|
||||||
"""This host should not be found, connect should fail."""
|
|
||||||
self._fail_set_apphosts = False
|
|
||||||
self._fail_ip = True
|
|
||||||
self._fail_domain_list = False
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.connect_volume,
|
|
||||||
cprops)
|
|
||||||
|
|
||||||
def test_connect_volume_nospace_fail(self):
|
|
||||||
"""The space command will fail, exception to be thrown"""
|
|
||||||
self._fail_set_apphosts = True
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.connect_volume,
|
|
||||||
cprops)
|
|
||||||
|
|
||||||
def test_disconnect_volume(self):
|
|
||||||
"""Simple disconnection should pass and disconnect me"""
|
|
||||||
self._fail_set_apphosts = False
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
self._cmdline = ""
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
self.connector.disconnect_volume(cprops, None)
|
|
||||||
exp_cli = ("vgc-cluster space-set-apphosts -n space "
|
|
||||||
"-A localhost --action DELETE")
|
|
||||||
self.assertEqual(exp_cli, self.cmdline)
|
|
||||||
|
|
||||||
def test_disconnect_volume_nohost(self):
|
|
||||||
"""Should not run a setapphosts because localhost will"""
|
|
||||||
"""be the noremotehost"""
|
|
||||||
self._fail_set_apphosts = False
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
self._cmdline = ""
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'localhost'}
|
|
||||||
self.connector.disconnect_volume(cprops, None)
|
|
||||||
# The last command should be the IP listing, not set apphosts
|
|
||||||
exp_cli = ("ip addr list")
|
|
||||||
self.assertEqual(exp_cli, self.cmdline)
|
|
||||||
|
|
||||||
def test_disconnect_volume_fails(self):
|
|
||||||
"""The set-apphosts should fail, exception to be thrown"""
|
|
||||||
self._fail_set_apphosts = True
|
|
||||||
self._fail_ip = False
|
|
||||||
self._fail_domain_list = False
|
|
||||||
self._cmdline = ""
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.disconnect_volume,
|
|
||||||
cprops, None)
|
|
||||||
|
|
||||||
def test_bad_connection_properties(self):
|
|
||||||
"""Send in connection_properties missing required fields"""
|
|
||||||
# Invalid connection_properties
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.connect_volume,
|
|
||||||
None)
|
|
||||||
# Name required for connect_volume
|
|
||||||
cprops = {'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.connect_volume,
|
|
||||||
cprops)
|
|
||||||
# Invalid connection_properties
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.disconnect_volume,
|
|
||||||
None, None)
|
|
||||||
# Name and noremovehost needed for disconnect_volume
|
|
||||||
cprops = {'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.disconnect_volume,
|
|
||||||
cprops, None)
|
|
||||||
cprops = {'name': 'space'}
|
|
||||||
self.assertRaises(exception.BrickException,
|
|
||||||
self.connector.disconnect_volume,
|
|
||||||
cprops, None)
|
|
||||||
|
|
||||||
def test_extend_volume(self):
|
|
||||||
cprops = {'name': 'space', 'noremovehost': 'stor1'}
|
|
||||||
self.assertRaises(NotImplementedError,
|
|
||||||
self.connector.extend_volume,
|
|
||||||
cprops)
|
|
||||||
6
releasenotes/notes/remove-hgst-daa7f07c307974d0.yaml
Normal file
6
releasenotes/notes/remove-hgst-daa7f07c307974d0.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The HGST driver was removed from Cinder in the Stein release.
|
||||||
|
The connector logic in os-brick is no longer needed and has now been
|
||||||
|
removed.
|
||||||
Reference in New Issue
Block a user