LINSTOR driver update for LINSTOR v0.9.12 with REST API
LINSTOR driver for Cinder now supports the latest version of LINSTOR with REST API on the backend. Change-Id: Icf04b1c515c766edc037ba6f4bfba5b370faebbe
This commit is contained in:
parent
9dd5232ea5
commit
92b43f9c68
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2014-2018 LINBIT HA Solutions GmbH
|
# Copyright (c) 2014-2019 LINBIT HA Solutions GmbH
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
"""This driver connects Cinder to an installed LINSTOR instance.
|
"""This driver connects Cinder to an installed LINSTOR instance.
|
||||||
|
|
||||||
See https://docs.linbit.com/docs/users-guide-9.0/#ch-openstack
|
See https://docs.linbit.com/docs/users-guide-9.0/#ch-openstack-linstor
|
||||||
for more details.
|
for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -34,11 +34,6 @@ from cinder import interface
|
|||||||
from cinder.volume import configuration
|
from cinder.volume import configuration
|
||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
|
|
||||||
try:
|
|
||||||
import google.protobuf.json_format as proto
|
|
||||||
except ImportError:
|
|
||||||
proto = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import linstor
|
import linstor
|
||||||
lin_drv = linstor.Linstor
|
lin_drv = linstor.Linstor
|
||||||
@ -69,7 +64,14 @@ linstor_opts = [
|
|||||||
default=4096,
|
default=4096,
|
||||||
help='Default Block size for Image restoration. '
|
help='Default Block size for Image restoration. '
|
||||||
'When using iSCSI transport, this option '
|
'When using iSCSI transport, this option '
|
||||||
'specifies the block size'),
|
'specifies the block size.'),
|
||||||
|
|
||||||
|
cfg.IntOpt('linstor_autoplace_count',
|
||||||
|
default=0,
|
||||||
|
help='Autoplace replication count on volume deployment. '
|
||||||
|
'0 = Full cluster replication without autoplace, '
|
||||||
|
'1 = Single node deployment without replication, '
|
||||||
|
'2 or greater = Replicated deployment with autoplace.'),
|
||||||
|
|
||||||
cfg.BoolOpt('linstor_controller_diskless',
|
cfg.BoolOpt('linstor_controller_diskless',
|
||||||
default=True,
|
default=True,
|
||||||
@ -84,14 +86,25 @@ CONF.register_opts(linstor_opts, group=configuration.SHARED_CONF_GROUP)
|
|||||||
CINDER_UNKNOWN = 'unknown'
|
CINDER_UNKNOWN = 'unknown'
|
||||||
DM_VN_PREFIX = 'CV_'
|
DM_VN_PREFIX = 'CV_'
|
||||||
DM_SN_PREFIX = 'SN_'
|
DM_SN_PREFIX = 'SN_'
|
||||||
LVM = 'Lvm'
|
DISKLESS = 'DISKLESS'
|
||||||
LVMTHIN = 'LvmThin'
|
LVM = 'LVM'
|
||||||
|
LVM_THIN = 'LVM_THIN'
|
||||||
|
ZFS = 'ZFS'
|
||||||
|
ZFS_THIN = 'ZFS_THIN'
|
||||||
|
|
||||||
|
|
||||||
class LinstorBaseDriver(driver.VolumeDriver):
|
class LinstorBaseDriver(driver.VolumeDriver):
|
||||||
"""Cinder driver that uses Linstor for storage."""
|
"""Cinder driver that uses LINSTOR for storage.
|
||||||
|
|
||||||
VERSION = '1.0.0'
|
Version history:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
1.0.0 - Initial driver
|
||||||
|
1.0.1 - Added support for LINSTOR 0.9.12
|
||||||
|
"""
|
||||||
|
|
||||||
|
VERSION = '1.0.1'
|
||||||
|
|
||||||
# ThirdPartySystems wiki page
|
# ThirdPartySystems wiki page
|
||||||
CI_WIKI_NAME = 'LINBIT_LINSTOR_CI'
|
CI_WIKI_NAME = 'LINBIT_LINSTOR_CI'
|
||||||
@ -113,6 +126,8 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
'linstor_default_blocksize')
|
'linstor_default_blocksize')
|
||||||
self.diskless = self.configuration.safe_get(
|
self.diskless = self.configuration.safe_get(
|
||||||
'linstor_controller_diskless')
|
'linstor_controller_diskless')
|
||||||
|
self.ap_count = self.configuration.safe_get(
|
||||||
|
'linstor_autoplace_count')
|
||||||
self.default_backend_name = self.configuration.safe_get(
|
self.default_backend_name = self.configuration.safe_get(
|
||||||
'volume_backend_name')
|
'volume_backend_name')
|
||||||
self.host_name = socket.gethostname()
|
self.host_name = socket.gethostname()
|
||||||
@ -176,44 +191,36 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
api_reply = lin.resource_list()[0].__dict__['_rest_data']
|
||||||
response = proto.MessageToDict(lin.resource_list()[0].proto_msg)
|
return api_reply
|
||||||
return response
|
|
||||||
|
|
||||||
def _get_api_resource_dfn_list(self):
|
def _get_api_resource_dfn_list(self):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
api_reply = lin.resource_dfn_list()[0].__dict__['_rest_data']
|
||||||
|
return api_reply
|
||||||
|
|
||||||
response = proto.MessageToDict(
|
def _get_api_node_list(self):
|
||||||
lin.resource_dfn_list()[0].proto_msg)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _get_api_nodes_list(self):
|
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
api_reply = lin.node_list()[0].__dict__['_rest_data']
|
||||||
response = proto.MessageToDict(lin.node_list()[0].proto_msg)
|
return api_reply
|
||||||
return response
|
|
||||||
|
|
||||||
def _get_api_storage_pool_dfn_list(self):
|
def _get_api_storage_pool_dfn_list(self):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
api_reply = lin.storage_pool_dfn_list()[0].__dict__['_rest_data']
|
||||||
response = proto.MessageToDict(
|
return api_reply
|
||||||
lin.storage_pool_dfn_list()[0].proto_msg)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _get_api_storage_pool_list(self):
|
def _get_api_storage_pool_list(self):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
api_reply = lin.storage_pool_list()[0].__dict__['_rest_data']
|
||||||
response = proto.MessageToDict(
|
return api_reply
|
||||||
lin.storage_pool_list()[0].proto_msg)
|
|
||||||
return response
|
|
||||||
|
|
||||||
def _get_api_volume_extend(self, rsc_target_name, new_size):
|
def _get_api_volume_extend(self, rsc_target_name, new_size):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
@ -226,25 +233,15 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
size=self._vol_size_to_linstor(new_size))
|
size=self._vol_size_to_linstor(new_size))
|
||||||
return vol_reply
|
return vol_reply
|
||||||
|
|
||||||
def _api_snapshot_create(self, node_names, rsc_name, snapshot_name):
|
def _api_snapshot_create(self, drbd_rsc_name, snapshot_name):
|
||||||
with lin_drv(self.default_uri) as lin:
|
lin = linstor.Resource(drbd_rsc_name, uri=self.default_uri)
|
||||||
if not lin.connected:
|
snap_reply = lin.snapshot_create(snapshot_name)
|
||||||
lin.connect()
|
return snap_reply
|
||||||
|
|
||||||
snap_reply = lin.snapshot_create(node_names=node_names,
|
def _api_snapshot_delete(self, drbd_rsc_name, snapshot_name):
|
||||||
rsc_name=rsc_name,
|
lin = linstor.Resource(drbd_rsc_name, uri=self.default_uri)
|
||||||
snapshot_name=snapshot_name,
|
snap_reply = lin.snapshot_delete(snapshot_name)
|
||||||
async_msg=False)
|
return snap_reply
|
||||||
return snap_reply
|
|
||||||
|
|
||||||
def _api_snapshot_delete(self, drbd_rsc_name, snap_name):
|
|
||||||
with lin_drv(self.default_uri) as lin:
|
|
||||||
if not lin.connected:
|
|
||||||
lin.connect()
|
|
||||||
|
|
||||||
snap_reply = lin.snapshot_delete(rsc_name=drbd_rsc_name,
|
|
||||||
snapshot_name=snap_name)
|
|
||||||
return snap_reply
|
|
||||||
|
|
||||||
def _api_rsc_dfn_delete(self, drbd_rsc_name):
|
def _api_rsc_dfn_delete(self, drbd_rsc_name):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
@ -320,6 +317,18 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
rsc_reply = lin.resource_create([new_rsc], async_msg=False)
|
rsc_reply = lin.resource_create([new_rsc], async_msg=False)
|
||||||
return rsc_reply
|
return rsc_reply
|
||||||
|
|
||||||
|
def _api_rsc_autoplace(self, rsc_name):
|
||||||
|
with lin_drv(self.default_uri) as lin:
|
||||||
|
if not lin.connected:
|
||||||
|
lin.connect()
|
||||||
|
|
||||||
|
new_rsc = linstor.Resource(name=rsc_name, uri=self.default_uri)
|
||||||
|
new_rsc.placement.redundancy = self.ap_count
|
||||||
|
new_rsc.placement.storage_pool = self.default_pool
|
||||||
|
rsc_reply = new_rsc.autoplace()
|
||||||
|
|
||||||
|
return rsc_reply
|
||||||
|
|
||||||
def _api_rsc_delete(self, rsc_name, node_name):
|
def _api_rsc_delete(self, rsc_name, node_name):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
@ -329,6 +338,36 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
rsc_name=rsc_name)
|
rsc_name=rsc_name)
|
||||||
return rsc_reply
|
return rsc_reply
|
||||||
|
|
||||||
|
def _api_rsc_auto_delete(self, rsc_name):
|
||||||
|
with lin_drv(self.default_uri) as lin:
|
||||||
|
if not lin.connected:
|
||||||
|
lin.connect()
|
||||||
|
|
||||||
|
rsc = linstor.Resource(str(rsc_name), self.default_uri)
|
||||||
|
return rsc.delete()
|
||||||
|
|
||||||
|
def _api_rsc_is_diskless(self, rsc_name):
|
||||||
|
with lin_drv(self.default_uri) as lin:
|
||||||
|
if not lin.connected:
|
||||||
|
lin.connect()
|
||||||
|
|
||||||
|
rsc = linstor.Resource(str(rsc_name))
|
||||||
|
return rsc.is_diskless(self.host_name)
|
||||||
|
|
||||||
|
def _api_rsc_size(self, rsc_name):
|
||||||
|
with lin_drv(self.default_uri) as lin:
|
||||||
|
if not lin.connected:
|
||||||
|
lin.connect()
|
||||||
|
|
||||||
|
rsc = linstor.Resource(str(rsc_name))
|
||||||
|
if len(rsc.volumes):
|
||||||
|
if "size" in rsc.volumes:
|
||||||
|
return rsc.volumes[0].size
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
def _api_volume_dfn_delete(self, rsc_name, volume_nr):
|
def _api_volume_dfn_delete(self, rsc_name, volume_nr):
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
@ -353,29 +392,39 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
return vol_reply
|
return vol_reply
|
||||||
|
|
||||||
def _api_snapshot_resource_restore(self,
|
def _api_snapshot_resource_restore(self,
|
||||||
nodes,
|
|
||||||
src_rsc_name,
|
src_rsc_name,
|
||||||
src_snap_name,
|
src_snap_name,
|
||||||
new_vol_name):
|
new_vol_name):
|
||||||
|
|
||||||
|
lin = linstor.Resource(src_rsc_name, uri=self.default_uri)
|
||||||
|
new_rsc = lin.restore_from_snapshot(src_snap_name, new_vol_name)
|
||||||
|
|
||||||
|
# Adds an aux/property KV for synchronous return from snapshot restore
|
||||||
with lin_drv(self.default_uri) as lin:
|
with lin_drv(self.default_uri) as lin:
|
||||||
if not lin.connected:
|
if not lin.connected:
|
||||||
lin.connect()
|
lin.connect()
|
||||||
|
|
||||||
rsc_reply = lin.snapshot_resource_restore(
|
aux_prop = {}
|
||||||
node_names=nodes,
|
aux_prop["Aux/restore"] = "done"
|
||||||
from_resource=src_rsc_name,
|
lin.volume_dfn_modify(
|
||||||
from_snapshot=src_snap_name,
|
rsc_name=new_vol_name,
|
||||||
to_resource=new_vol_name)
|
volume_nr=0,
|
||||||
return rsc_reply
|
set_properties=aux_prop)
|
||||||
|
|
||||||
|
if new_rsc.name == new_vol_name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _get_rsc_path(self, rsc_name):
|
def _get_rsc_path(self, rsc_name):
|
||||||
rsc_list_reply = self._get_api_resource_list()
|
rsc_list_reply = self._get_api_resource_list()
|
||||||
|
|
||||||
for rsc in rsc_list_reply['resources']:
|
if rsc_list_reply:
|
||||||
if rsc['name'] == rsc_name and rsc['nodeName'] == self.host_name:
|
for rsc in rsc_list_reply:
|
||||||
for volume in rsc['vlms']:
|
if (rsc["name"] == rsc_name and
|
||||||
if volume['vlmNr'] == 0:
|
rsc["node_name"] == self.host_name):
|
||||||
return volume['devicePath']
|
for volume in rsc["volumes"]:
|
||||||
|
if volume["volume_number"] == 0:
|
||||||
|
return volume["device_path"]
|
||||||
|
|
||||||
def _get_local_path(self, volume):
|
def _get_local_path(self, volume):
|
||||||
try:
|
try:
|
||||||
@ -391,13 +440,11 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
def _get_spd(self):
|
def _get_spd(self):
|
||||||
# Storage Pool Definition List
|
# Storage Pool Definition List
|
||||||
spd_list_reply = self._get_api_storage_pool_dfn_list()
|
spd_list_reply = self._get_api_storage_pool_dfn_list()
|
||||||
|
|
||||||
spd_list = []
|
spd_list = []
|
||||||
for node in spd_list_reply['storPoolDfns']:
|
|
||||||
spd_item = {}
|
if spd_list_reply:
|
||||||
spd_item['spd_uuid'] = node['uuid']
|
for spd in spd_list_reply:
|
||||||
spd_item['spd_name'] = node['storPoolName']
|
spd_list.append(spd["storage_pool_name"])
|
||||||
spd_list.append(spd_item)
|
|
||||||
|
|
||||||
return spd_list
|
return spd_list
|
||||||
|
|
||||||
@ -405,49 +452,39 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
# Fetch Storage Pool List
|
# Fetch Storage Pool List
|
||||||
sp_list_reply = self._get_api_storage_pool_list()
|
sp_list_reply = self._get_api_storage_pool_list()
|
||||||
|
|
||||||
# Fetch Resource Definition List
|
|
||||||
sp_list = []
|
|
||||||
|
|
||||||
# Separate the diskless nodes
|
# Separate the diskless nodes
|
||||||
sp_diskless_list = []
|
sp_diskless_list = []
|
||||||
|
sp_list = []
|
||||||
node_count = 0
|
node_count = 0
|
||||||
|
|
||||||
if sp_list_reply:
|
if sp_list_reply:
|
||||||
for node in sp_list_reply['storPools']:
|
for node in sp_list_reply:
|
||||||
if node['storPoolName'] == self.default_pool:
|
if node["storage_pool_name"] == self.default_pool:
|
||||||
sp_node = {}
|
sp_node = {}
|
||||||
sp_node['node_uuid'] = node['nodeUuid']
|
sp_node["node_name"] = node["node_name"]
|
||||||
sp_node['node_name'] = node['nodeName']
|
sp_node["sp_uuid"] = node["uuid"]
|
||||||
sp_node['sp_uuid'] = node['storPoolUuid']
|
sp_node["sp_name"] = node["storage_pool_name"]
|
||||||
sp_node['sp_name'] = node['storPoolName']
|
|
||||||
sp_node['sp_vlms_uuid'] = []
|
|
||||||
if 'vlms' in node:
|
|
||||||
for vlm in node['vlms']:
|
|
||||||
sp_node['sp_vlms_uuid'].append(vlm['vlmDfnUuid'])
|
|
||||||
|
|
||||||
if 'Diskless' in node['driver']:
|
if node["provider_kind"] == DISKLESS:
|
||||||
diskless = True
|
diskless = True
|
||||||
sp_node['sp_free'] = -1.0
|
sp_node["sp_free"] = -1.0
|
||||||
sp_node['sp_cap'] = 0.0
|
sp_node["sp_cap"] = -1.0
|
||||||
|
sp_node["sp_allocated"] = 0.0
|
||||||
else:
|
else:
|
||||||
diskless = False
|
diskless = False
|
||||||
if 'freeSpace' in node:
|
if "free_capacity" in node:
|
||||||
sp_node['sp_free'] = round(
|
temp = float(node["free_capacity"]) / units.Mi
|
||||||
int(node['freeSpace']['freeCapacity']) /
|
sp_node["sp_free"] = round(temp)
|
||||||
units.Mi,
|
temp = float(node["total_capacity"]) / units.Mi
|
||||||
2)
|
sp_node["sp_cap"] = round(temp)
|
||||||
sp_node['sp_cap'] = round(
|
|
||||||
int(node['freeSpace']['totalCapacity']) /
|
|
||||||
units.Mi,
|
|
||||||
2)
|
|
||||||
|
|
||||||
# Driver
|
drivers = [LVM, LVM_THIN, ZFS, ZFS_THIN, DISKLESS]
|
||||||
if node['driver'] == "LvmDriver":
|
|
||||||
sp_node['driver_name'] = LVM
|
# Driver selection
|
||||||
elif node['driver'] == "LvmThinDriver":
|
if node["provider_kind"] in drivers:
|
||||||
sp_node['driver_name'] = LVMTHIN
|
sp_node['driver_name'] = node["provider_kind"]
|
||||||
else:
|
else:
|
||||||
sp_node['driver_name'] = node['driver']
|
sp_node['driver_name'] = str(node["provider_kind"])
|
||||||
|
|
||||||
if diskless:
|
if diskless:
|
||||||
sp_diskless_list.append(sp_node)
|
sp_diskless_list.append(sp_node)
|
||||||
@ -455,9 +492,9 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
sp_list.append(sp_node)
|
sp_list.append(sp_node)
|
||||||
node_count += 1
|
node_count += 1
|
||||||
|
|
||||||
# Add the diskless nodes to the end of the list
|
# Add the diskless nodes to the end of the list
|
||||||
if sp_diskless_list:
|
if sp_diskless_list:
|
||||||
sp_list.extend(sp_diskless_list)
|
sp_list.extend(sp_diskless_list)
|
||||||
|
|
||||||
return sp_list
|
return sp_list
|
||||||
|
|
||||||
@ -465,7 +502,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
data["volume_backend_name"] = self.default_backend_name
|
data["volume_backend_name"] = self.default_backend_name
|
||||||
data["vendor_name"] = 'LINBIT'
|
data["vendor_name"] = "LINBIT"
|
||||||
data["driver_version"] = self.VERSION
|
data["driver_version"] = self.VERSION
|
||||||
data["pools"] = []
|
data["pools"] = []
|
||||||
|
|
||||||
@ -477,40 +514,51 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
for rd in rd_list:
|
for rd in rd_list:
|
||||||
num_vols += 1
|
num_vols += 1
|
||||||
|
|
||||||
allocated_sizes_gb = []
|
# allocated_sizes_gb = []
|
||||||
free_capacity_gb = []
|
free_gb = []
|
||||||
total_capacity_gb = []
|
total_gb = []
|
||||||
thin_enabled = False
|
thin_enabled = False
|
||||||
|
|
||||||
# Free capacity for Local Node
|
# Total & Free capacity for Local Node
|
||||||
single_pool = {}
|
single_pool = {}
|
||||||
for sp in sp_data:
|
for sp in sp_data:
|
||||||
if 'Diskless' not in sp['driver_name']:
|
if "Diskless" not in sp["driver_name"]:
|
||||||
if 'LvmThin' in sp['driver_name']:
|
thin_backends = [LVM_THIN, ZFS_THIN]
|
||||||
|
if sp["driver_name"] in thin_backends:
|
||||||
thin_enabled = True
|
thin_enabled = True
|
||||||
if 'sp_cap' in sp:
|
if "sp_cap" in sp:
|
||||||
if sp['sp_cap'] >= 0.0:
|
if sp["sp_cap"] >= 0.0:
|
||||||
total_capacity_gb.append(sp['sp_cap'])
|
total_gb.append(sp["sp_cap"])
|
||||||
if 'sp_free' in sp:
|
if "sp_free" in sp:
|
||||||
if sp['sp_free'] >= 0.0:
|
if sp["sp_free"] >= 0.0:
|
||||||
free_capacity_gb.append(sp['sp_free'])
|
free_gb.append(sp["sp_free"])
|
||||||
sp_allocated_size_gb = 0
|
|
||||||
for vlm_uuid in sp['sp_vlms_uuid']:
|
# Allocated capacity
|
||||||
for rd in rd_list:
|
sp_allocated_size_gb = 0.0
|
||||||
if 'vlm_dfn_uuid' in rd:
|
local_resources = []
|
||||||
if rd['vlm_dfn_uuid'] == vlm_uuid:
|
|
||||||
sp_allocated_size_gb += rd['rd_size']
|
reply = self._get_api_resource_list()
|
||||||
allocated_sizes_gb.append(sp_allocated_size_gb)
|
|
||||||
|
if reply:
|
||||||
|
for rsc in reply:
|
||||||
|
if rsc["node_name"] == self.host_name:
|
||||||
|
local_resources.append(rsc["name"])
|
||||||
|
|
||||||
|
for rsc_name in local_resources:
|
||||||
|
if not self._api_rsc_is_diskless(rsc_name):
|
||||||
|
rsc_size = self._api_rsc_size(rsc_name)
|
||||||
|
sp_allocated_size_gb += round(
|
||||||
|
int(rsc_size) / units.Gi, 2)
|
||||||
|
|
||||||
single_pool["pool_name"] = data["volume_backend_name"]
|
single_pool["pool_name"] = data["volume_backend_name"]
|
||||||
single_pool["free_capacity_gb"] = min(free_capacity_gb)
|
single_pool["free_capacity_gb"] = min(free_gb) if free_gb else 0
|
||||||
single_pool["total_capacity_gb"] = min(total_capacity_gb)
|
single_pool["total_capacity_gb"] = min(total_gb) if total_gb else 0
|
||||||
single_pool['provisioned_capacity_gb'] = max(allocated_sizes_gb)
|
single_pool["provisioned_capacity_gb"] = sp_allocated_size_gb
|
||||||
single_pool["reserved_percentage"] = (
|
single_pool["reserved_percentage"] = (
|
||||||
self.configuration.reserved_percentage)
|
self.configuration.reserved_percentage)
|
||||||
single_pool['thin_provisioning_support'] = thin_enabled
|
single_pool["thin_provisioning_support"] = thin_enabled
|
||||||
single_pool['thick_provisioning_support'] = not thin_enabled
|
single_pool["thick_provisioning_support"] = not thin_enabled
|
||||||
single_pool['max_over_subscription_ratio'] = (
|
single_pool["max_over_subscription_ratio"] = (
|
||||||
self.configuration.max_over_subscription_ratio)
|
self.configuration.max_over_subscription_ratio)
|
||||||
single_pool["location_info"] = self.default_uri
|
single_pool["location_info"] = self.default_uri
|
||||||
single_pool["total_volumes"] = num_vols
|
single_pool["total_volumes"] = num_vols
|
||||||
@ -518,7 +566,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
single_pool["goodness_function"] = self.get_goodness_function()
|
single_pool["goodness_function"] = self.get_goodness_function()
|
||||||
single_pool["QoS_support"] = False
|
single_pool["QoS_support"] = False
|
||||||
single_pool["multiattach"] = False
|
single_pool["multiattach"] = False
|
||||||
single_pool["backend_state"] = 'up'
|
single_pool["backend_state"] = "up"
|
||||||
|
|
||||||
data["pools"].append(single_pool)
|
data["pools"].append(single_pool)
|
||||||
|
|
||||||
@ -526,29 +574,16 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
def _get_resource_definitions(self):
|
def _get_resource_definitions(self):
|
||||||
|
|
||||||
|
rd_list_reply = self._get_api_resource_dfn_list()
|
||||||
rd_list = []
|
rd_list = []
|
||||||
|
|
||||||
rd_list_reply = self._get_api_resource_dfn_list()
|
if rd_list_reply:
|
||||||
|
for node in rd_list_reply:
|
||||||
# Only if resource definition present
|
|
||||||
if 'rscDfns' in rd_list_reply:
|
|
||||||
for node in rd_list_reply['rscDfns']:
|
|
||||||
|
|
||||||
# Count only Cinder volumes
|
# Count only Cinder volumes
|
||||||
if DM_VN_PREFIX in node['rscName']:
|
if DM_VN_PREFIX in node['name']:
|
||||||
rd_node = {}
|
rd_node = {}
|
||||||
rd_node['rd_uuid'] = node['rscDfnUuid']
|
rd_node["rd_uuid"] = node['uuid']
|
||||||
rd_node['rd_name'] = node['rscName']
|
rd_node["rd_name"] = node['name']
|
||||||
rd_node['rd_port'] = node['rscDfnPort']
|
|
||||||
|
|
||||||
if 'vlmDfns' in node:
|
|
||||||
for vol in node['vlmDfns']:
|
|
||||||
if vol['vlmNr'] == 0:
|
|
||||||
rd_node['vlm_dfn_uuid'] = vol['vlmDfnUuid']
|
|
||||||
rd_node['rd_size'] = round(
|
|
||||||
float(vol['vlmSize']) / units.Mi, 2)
|
|
||||||
break
|
|
||||||
|
|
||||||
rd_list.append(rd_node)
|
rd_list.append(rd_node)
|
||||||
|
|
||||||
return rd_list
|
return rd_list
|
||||||
@ -558,46 +593,62 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
However, it excludes diskless nodes.
|
However, it excludes diskless nodes.
|
||||||
"""
|
"""
|
||||||
|
rsc_list_reply = self._get_api_resource_list()
|
||||||
rsc_list_reply = self._get_api_resource_list() # reply in dict
|
|
||||||
|
|
||||||
snap_list = []
|
snap_list = []
|
||||||
for rsc in rsc_list_reply['resources']:
|
|
||||||
if rsc['name'] != resource:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Diskless nodes are not available for snapshots
|
if rsc_list_reply:
|
||||||
diskless = False
|
for rsc in rsc_list_reply:
|
||||||
if 'rscFlags' in rsc:
|
if rsc["name"] != resource:
|
||||||
if 'DISKLESS' in rsc['rscFlags']:
|
continue
|
||||||
diskless = True
|
|
||||||
if not diskless:
|
# Diskless nodes are not available for snapshots
|
||||||
snap_list.append(rsc['nodeName'])
|
diskless = False
|
||||||
|
if "flags" in rsc:
|
||||||
|
if 'DISKLESS' in rsc["flags"]:
|
||||||
|
diskless = True
|
||||||
|
if not diskless:
|
||||||
|
snap_list.append(rsc["node_name"])
|
||||||
|
|
||||||
return snap_list
|
return snap_list
|
||||||
|
|
||||||
def _get_linstor_nodes(self):
|
def _get_diskless_nodes(self, resource):
|
||||||
# Returns all available DRBD nodes
|
# Returns diskless nodes given a resource
|
||||||
node_list_reply = self._get_api_nodes_list()
|
rsc_list_reply = self._get_api_resource_list()
|
||||||
|
diskless_list = []
|
||||||
|
|
||||||
|
if rsc_list_reply:
|
||||||
|
for rsc in rsc_list_reply:
|
||||||
|
if rsc["name"] != resource:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "flags" in rsc:
|
||||||
|
if DISKLESS in rsc["flags"]:
|
||||||
|
diskless_list.append(rsc["node_name"])
|
||||||
|
|
||||||
|
return diskless_list
|
||||||
|
|
||||||
|
def _get_linstor_nodes(self):
|
||||||
|
# Returns all available LINSTOR nodes
|
||||||
|
node_list_reply = self._get_api_node_list()
|
||||||
node_list = []
|
node_list = []
|
||||||
for node in node_list_reply['nodes']:
|
|
||||||
node_list.append(node['name'])
|
if node_list_reply:
|
||||||
|
for node in node_list_reply:
|
||||||
|
node_list.append(node["name"])
|
||||||
|
|
||||||
return node_list
|
return node_list
|
||||||
|
|
||||||
def _get_nodes(self):
|
def _get_nodes(self):
|
||||||
# Get Node List
|
# Returns all LINSTOR nodes in a dict list
|
||||||
node_list_reply = self._get_api_nodes_list()
|
node_list_reply = self._get_api_node_list()
|
||||||
|
|
||||||
node_list = []
|
node_list = []
|
||||||
|
|
||||||
if node_list_reply:
|
if node_list_reply:
|
||||||
for node in node_list_reply['nodes']:
|
for node in node_list_reply:
|
||||||
node_item = {}
|
node_item = {}
|
||||||
node_item['node_name'] = node['name']
|
node_item["node_name"] = node["name"]
|
||||||
node_item['node_uuid'] = node['uuid']
|
node_item["node_address"] = (
|
||||||
node_item['node_address'] = (
|
node["net_interfaces"][0]["address"])
|
||||||
node['netInterfaces'][0]['address'])
|
|
||||||
node_list.append(node_item)
|
node_list.append(node_item)
|
||||||
|
|
||||||
return node_list
|
return node_list
|
||||||
@ -622,83 +673,57 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
#
|
#
|
||||||
def create_snapshot(self, snapshot):
|
def create_snapshot(self, snapshot):
|
||||||
snap_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
snap_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
||||||
drbd_rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
||||||
node_names = self._get_snapshot_nodes(drbd_rsc_name)
|
|
||||||
|
|
||||||
snap_reply = self._api_snapshot_create(node_names=node_names,
|
snap_reply = self._api_snapshot_create(drbd_rsc_name=rsc_name,
|
||||||
rsc_name=drbd_rsc_name,
|
|
||||||
snapshot_name=snap_name)
|
snapshot_name=snap_name)
|
||||||
|
|
||||||
if not self._check_api_reply(snap_reply, noerror_only=True):
|
if not snap_reply:
|
||||||
msg = 'ERROR creating a LINSTOR snapshot {}'.format(snap_name)
|
msg = 'ERROR creating a LINSTOR snapshot {}'.format(snap_name)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(msg)
|
raise exception.VolumeBackendAPIException(msg)
|
||||||
|
|
||||||
def delete_snapshot(self, snapshot):
|
def delete_snapshot(self, snapshot):
|
||||||
snap_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
snapshot_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
||||||
drbd_rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
||||||
|
|
||||||
snap_reply = self._api_snapshot_delete(drbd_rsc_name, snap_name)
|
snap_reply = self._api_snapshot_delete(rsc_name, snapshot_name)
|
||||||
|
|
||||||
if not self._check_api_reply(snap_reply, noerror_only=True):
|
if not snap_reply:
|
||||||
msg = 'ERROR deleting a LINSTOR snapshot {}'.format(snap_name)
|
msg = 'ERROR deleting a LINSTOR snapshot {}'.format(snapshot_name)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(msg)
|
raise exception.VolumeBackendAPIException(msg)
|
||||||
|
|
||||||
# Delete RD if no other RSC are found
|
# Delete RD if no other RSC are found
|
||||||
if not self._get_snapshot_nodes(drbd_rsc_name):
|
if not self._get_snapshot_nodes(rsc_name):
|
||||||
self._api_rsc_dfn_delete(drbd_rsc_name)
|
self._api_rsc_dfn_delete(rsc_name)
|
||||||
|
|
||||||
def create_volume_from_snapshot(self, volume, snapshot):
|
def create_volume_from_snapshot(self, volume, snapshot):
|
||||||
src_rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
src_rsc_name = self._drbd_resource_name_from_cinder_snapshot(snapshot)
|
||||||
src_snap_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
src_snap_name = self._snapshot_name_from_cinder_snapshot(snapshot)
|
||||||
new_vol_name = self._drbd_resource_name_from_cinder_volume(volume)
|
new_vol_name = self._drbd_resource_name_from_cinder_volume(volume)
|
||||||
|
|
||||||
# New RD
|
# If no autoplace, manually build a cluster list
|
||||||
rsc_reply = self._api_rsc_dfn_create(new_vol_name)
|
if self.ap_count == 0:
|
||||||
|
diskless_nodes = []
|
||||||
|
nodes = []
|
||||||
|
for node in self._get_storage_pool():
|
||||||
|
|
||||||
if not self._check_api_reply(rsc_reply):
|
if DISKLESS in node['driver_name']:
|
||||||
msg = _('Error on creating LINSTOR Resource Definition')
|
diskless_nodes.append(node['node_name'])
|
||||||
LOG.error(msg)
|
continue
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
|
|
||||||
# New VD from Snap
|
# Filter out controller node if it is diskless
|
||||||
reply = self._api_snapshot_volume_dfn_restore(src_rsc_name,
|
if self.diskless and node['node_name'] == self.host_name:
|
||||||
src_snap_name,
|
continue
|
||||||
new_vol_name)
|
else:
|
||||||
if not self._check_api_reply(reply, noerror_only=True):
|
nodes.append(node['node_name'])
|
||||||
msg = _('Error on restoring LINSTOR Volume Definition')
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
|
|
||||||
# Set StorPoolName property on VD
|
reply = self._api_snapshot_resource_restore(src_rsc_name,
|
||||||
reply = self._api_volume_dfn_set_sp(new_vol_name)
|
|
||||||
if not self._check_api_reply(reply):
|
|
||||||
msg = _('Error on restoring LINSTOR Volume StorPoolName property')
|
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
|
|
||||||
# New RSC from Snap
|
|
||||||
# Assumes restoring to all the nodes containing the storage pool
|
|
||||||
# unless diskless
|
|
||||||
nodes = []
|
|
||||||
for node in self._get_storage_pool():
|
|
||||||
|
|
||||||
if 'Diskless' in node['driver_name']:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Filter out controller node if LINSTOR is diskless
|
|
||||||
if self.diskless and node['node_name'] == self.host_name:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
nodes.append(node['node_name'])
|
|
||||||
|
|
||||||
reply = self._api_snapshot_resource_restore(nodes,
|
|
||||||
src_rsc_name,
|
|
||||||
src_snap_name,
|
src_snap_name,
|
||||||
new_vol_name)
|
new_vol_name)
|
||||||
if not self._check_api_reply(reply, noerror_only=True):
|
if not reply:
|
||||||
msg = _('Error on restoring LINSTOR resources')
|
msg = _('Error on restoring a LINSTOR volume')
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
@ -708,6 +733,13 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
node_name=self.host_name,
|
node_name=self.host_name,
|
||||||
diskless=self.diskless)
|
diskless=self.diskless)
|
||||||
|
|
||||||
|
# Add any other diskless nodes only if not autoplaced
|
||||||
|
if self.ap_count == 0 and diskless_nodes:
|
||||||
|
for node in diskless_nodes:
|
||||||
|
self._api_rsc_create(rsc_name=new_vol_name,
|
||||||
|
node_name=node,
|
||||||
|
diskless=True)
|
||||||
|
|
||||||
# Upsize if larger volume than original snapshot
|
# Upsize if larger volume than original snapshot
|
||||||
src_rsc_size = int(snapshot['volume_size'])
|
src_rsc_size = int(snapshot['volume_size'])
|
||||||
new_vol_size = int(volume['size'])
|
new_vol_size = int(volume['size'])
|
||||||
@ -722,7 +754,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
if not self._check_api_reply(reply, noerror_only=True):
|
if not self._check_api_reply(reply, noerror_only=True):
|
||||||
# Delete failed volume
|
# Delete failed volume
|
||||||
failed_volume = []
|
failed_volume = {}
|
||||||
failed_volume['id'] = volume['id']
|
failed_volume['id'] = volume['id']
|
||||||
self.delete_volume(failed_volume)
|
self.delete_volume(failed_volume)
|
||||||
|
|
||||||
@ -731,9 +763,9 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
|
|
||||||
# Check for Storage Pool List
|
# Check for Storage Pool List
|
||||||
sp_data = self._get_storage_pool()
|
sp_data = self._get_storage_pool()
|
||||||
rsc_size = 1
|
|
||||||
rsc_size = volume['size']
|
rsc_size = volume['size']
|
||||||
|
|
||||||
# No existing Storage Pools found
|
# No existing Storage Pools found
|
||||||
@ -747,11 +779,11 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
# Create Storage Pool (definition is implicit)
|
# Create Storage Pool
|
||||||
spd_list = self._get_spd()
|
spd_list = self._get_spd()
|
||||||
|
|
||||||
if spd_list:
|
if spd_list:
|
||||||
spd_name = spd_list[0]['spd_name']
|
spd_name = spd_list[0]
|
||||||
|
|
||||||
for node in node_list:
|
for node in node_list:
|
||||||
|
|
||||||
@ -766,12 +798,12 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
storage_driver=node_driver,
|
storage_driver=node_driver,
|
||||||
driver_pool_name=self.default_vg_name)
|
driver_pool_name=self.default_vg_name)
|
||||||
|
|
||||||
if not self._check_api_reply(sp_reply):
|
if not self._check_api_reply(sp_reply, noerror_only=True):
|
||||||
msg = _('Could not create a LINSTOR storage pool')
|
msg = _('Could not create a LINSTOR storage pool')
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
# # Check for RD
|
# Check for RD
|
||||||
# If Retyping from another volume, use parent/origin uuid
|
# If Retyping from another volume, use parent/origin uuid
|
||||||
# as a name source
|
# as a name source
|
||||||
if (volume['migration_status'] is not None and
|
if (volume['migration_status'] is not None and
|
||||||
@ -785,8 +817,8 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
|
|
||||||
# Create a New RD
|
# Create a New RD
|
||||||
rsc_dfn_reply = self._api_rsc_dfn_create(rsc_name)
|
rsc_dfn_reply = self._api_rsc_dfn_create(rsc_name)
|
||||||
if not self._check_api_reply(rsc_dfn_reply,
|
|
||||||
noerror_only=True):
|
if not self._check_api_reply(rsc_dfn_reply, noerror_only=True):
|
||||||
msg = _("Error creating a LINSTOR resource definition")
|
msg = _("Error creating a LINSTOR resource definition")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
@ -795,8 +827,8 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
vd_size = self._vol_size_to_linstor(rsc_size)
|
vd_size = self._vol_size_to_linstor(rsc_size)
|
||||||
vd_reply = self._api_volume_dfn_create(rsc_name=rsc_name,
|
vd_reply = self._api_volume_dfn_create(rsc_name=rsc_name,
|
||||||
size=int(vd_size))
|
size=int(vd_size))
|
||||||
if not self._check_api_reply(vd_reply,
|
|
||||||
noerror_only=True):
|
if not self._check_api_reply(vd_reply, noerror_only=True):
|
||||||
msg = _("Error creating a LINSTOR volume definition")
|
msg = _("Error creating a LINSTOR volume definition")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
@ -804,26 +836,38 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
# Create LINSTOR Resources
|
# Create LINSTOR Resources
|
||||||
ctrl_in_sp = False
|
ctrl_in_sp = False
|
||||||
for node in sp_data:
|
for node in sp_data:
|
||||||
|
|
||||||
# Check if controller is in the pool
|
# Check if controller is in the pool
|
||||||
if node['node_name'] == self.host_name:
|
if node['node_name'] == self.host_name:
|
||||||
ctrl_in_sp = True
|
ctrl_in_sp = True
|
||||||
|
|
||||||
# Create resources and,
|
# Use autoplace to deploy if set
|
||||||
# Check only errors when creating diskless resources
|
if self.ap_count:
|
||||||
if 'Diskless' in node['driver_name']:
|
try:
|
||||||
diskless = True
|
self._api_rsc_autoplace(rsc_name=rsc_name)
|
||||||
else:
|
|
||||||
diskless = False
|
|
||||||
rsc_reply = self._api_rsc_create(rsc_name=rsc_name,
|
|
||||||
node_name=node['node_name'],
|
|
||||||
diskless=diskless)
|
|
||||||
|
|
||||||
if not self._check_api_reply(rsc_reply, noerror_only=True):
|
except Exception:
|
||||||
msg = _("Error creating a LINSTOR resource")
|
msg = _("Error creating autoplaces LINSTOR resource(s)")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
# Otherwise deploy across the entire cluster
|
||||||
|
else:
|
||||||
|
for node in sp_data:
|
||||||
|
# Deploy resource on each node
|
||||||
|
if DISKLESS in node['driver_name']:
|
||||||
|
diskless = True
|
||||||
|
else:
|
||||||
|
diskless = False
|
||||||
|
|
||||||
|
rsc_reply = self._api_rsc_create(rsc_name=rsc_name,
|
||||||
|
node_name=node['node_name'],
|
||||||
|
diskless=diskless)
|
||||||
|
|
||||||
|
if not self._check_api_reply(rsc_reply, noerror_only=True):
|
||||||
|
msg = _("Error creating a LINSTOR resource")
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
# If the controller is diskless and not in the pool, create a diskless
|
# If the controller is diskless and not in the pool, create a diskless
|
||||||
# resource on it
|
# resource on it
|
||||||
if not ctrl_in_sp and self.diskless:
|
if not ctrl_in_sp and self.diskless:
|
||||||
@ -832,7 +876,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
diskless=True)
|
diskless=True)
|
||||||
|
|
||||||
if not self._check_api_reply(rsc_reply, noerror_only=True):
|
if not self._check_api_reply(rsc_reply, noerror_only=True):
|
||||||
msg = _("Error creating a LINSTOR resource")
|
msg = _("Error creating a LINSTOR controller resource")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
@ -841,32 +885,56 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
drbd_rsc_name = self._drbd_resource_name_from_cinder_volume(volume)
|
drbd_rsc_name = self._drbd_resource_name_from_cinder_volume(volume)
|
||||||
rsc_list_reply = self._get_api_resource_list()
|
rsc_list_reply = self._get_api_resource_list()
|
||||||
|
diskful_nodes = self._get_snapshot_nodes(drbd_rsc_name)
|
||||||
|
diskless_nodes = self._get_diskless_nodes(drbd_rsc_name)
|
||||||
|
|
||||||
if rsc_list_reply:
|
# If autoplace was used, use Resource class
|
||||||
# Delete Resources
|
if self.ap_count:
|
||||||
for rsc in rsc_list_reply['resources']:
|
|
||||||
if rsc['name'] != drbd_rsc_name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
rsc_reply = self._api_rsc_delete(
|
rsc_reply = self._api_rsc_auto_delete(drbd_rsc_name)
|
||||||
node_name=rsc['nodeName'],
|
if not rsc_reply:
|
||||||
rsc_name=drbd_rsc_name)
|
msg = _("Error deleting an autoplaced LINSTOR resource")
|
||||||
if not self._check_api_reply(rsc_reply, noerror_only=True):
|
LOG.error(msg)
|
||||||
msg = _("Error deleting a LINSTOR resource")
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
LOG.error(msg)
|
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
|
|
||||||
# Delete VD
|
# Delete all resources in a cluster manually if not autoplaced
|
||||||
vd_reply = self._api_volume_dfn_delete(drbd_rsc_name, 0)
|
else:
|
||||||
if not vd_reply:
|
if rsc_list_reply:
|
||||||
if not self._check_api_reply(vd_reply):
|
# Remove diskless nodes first
|
||||||
msg = _("Error deleting a LINSTOR volume definition")
|
if diskless_nodes:
|
||||||
LOG.error(msg)
|
for node in diskless_nodes:
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
rsc_reply = self._api_rsc_delete(
|
||||||
|
node_name=node,
|
||||||
|
rsc_name=drbd_rsc_name)
|
||||||
|
if not self._check_api_reply(rsc_reply,
|
||||||
|
noerror_only=True):
|
||||||
|
msg = _("Error deleting a diskless LINSTOR rsc")
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
# Delete RD
|
# Remove diskful nodes
|
||||||
# Will fail if snapshot exists but expected
|
if diskful_nodes:
|
||||||
self._api_rsc_dfn_delete(drbd_rsc_name)
|
for node in diskful_nodes:
|
||||||
|
rsc_reply = self._api_rsc_delete(
|
||||||
|
node_name=node,
|
||||||
|
rsc_name=drbd_rsc_name)
|
||||||
|
if not self._check_api_reply(rsc_reply,
|
||||||
|
noerror_only=True):
|
||||||
|
msg = _("Error deleting a LINSTOR resource")
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
# Delete VD
|
||||||
|
vd_reply = self._api_volume_dfn_delete(drbd_rsc_name, 0)
|
||||||
|
if not vd_reply:
|
||||||
|
if not self._check_api_reply(vd_reply):
|
||||||
|
msg = _("Error deleting a LINSTOR volume definition")
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
# Delete RD
|
||||||
|
# Will fail if snapshot exists but expected
|
||||||
|
self._api_rsc_dfn_delete(drbd_rsc_name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -877,7 +945,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
extend_reply = self._get_api_volume_extend(rsc_target_name, new_size)
|
extend_reply = self._get_api_volume_extend(rsc_target_name, new_size)
|
||||||
|
|
||||||
if not self._check_api_reply(extend_reply, noerror_only=True):
|
if not self._check_api_reply(extend_reply, noerror_only=True):
|
||||||
msg = _("ERROR Linstor Volume Extend")
|
msg = _("ERROR extending a LINSTOR volume")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
@ -895,11 +963,10 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
self.delete_snapshot(snapshot)
|
self.delete_snapshot(snapshot)
|
||||||
|
|
||||||
def copy_image_to_volume(self, context, volume, image_service, image_id):
|
def copy_image_to_volume(self, context, volume, image_service, image_id):
|
||||||
# self.create_volume(volume) already called by Cinder, and works.
|
# self.create_volume(volume) already called by Cinder, and works
|
||||||
# Need to check return values
|
|
||||||
full_rsc_name = self._drbd_resource_name_from_cinder_volume(volume)
|
full_rsc_name = self._drbd_resource_name_from_cinder_volume(volume)
|
||||||
|
|
||||||
# This creates a LINSTOR volume at the original size.
|
# This creates a LINSTOR volume from the source image
|
||||||
image_utils.fetch_to_raw(context,
|
image_utils.fetch_to_raw(context,
|
||||||
image_service,
|
image_service,
|
||||||
image_id,
|
image_id,
|
||||||
@ -923,14 +990,10 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
return (False, None)
|
return (False, None)
|
||||||
|
|
||||||
def check_for_setup_error(self):
|
def check_for_setup_error(self):
|
||||||
|
|
||||||
msg = None
|
msg = None
|
||||||
if linstor is None:
|
if linstor is None:
|
||||||
msg = _('Linstor python package not found')
|
msg = _('Linstor python package not found')
|
||||||
|
|
||||||
if proto is None:
|
|
||||||
msg = _('Protobuf python package not found')
|
|
||||||
|
|
||||||
if msg is not None:
|
if msg is not None:
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.VolumeDriverException(message=msg)
|
raise exception.VolumeDriverException(message=msg)
|
||||||
@ -954,7 +1017,7 @@ class LinstorBaseDriver(driver.VolumeDriver):
|
|||||||
# Class with iSCSI interface methods
|
# Class with iSCSI interface methods
|
||||||
@interface.volumedriver
|
@interface.volumedriver
|
||||||
class LinstorIscsiDriver(LinstorBaseDriver):
|
class LinstorIscsiDriver(LinstorBaseDriver):
|
||||||
"""Cinder iSCSI driver that uses Linstor for storage."""
|
"""Cinder iSCSI driver that uses LINSTOR for storage."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LinstorIscsiDriver, self).__init__(*args, **kwargs)
|
super(LinstorIscsiDriver, self).__init__(*args, **kwargs)
|
||||||
@ -965,7 +1028,7 @@ class LinstorIscsiDriver(LinstorBaseDriver):
|
|||||||
self.helper_driver = self.helper_name
|
self.helper_driver = self.helper_name
|
||||||
self.target_driver = None
|
self.target_driver = None
|
||||||
else:
|
else:
|
||||||
self.helper_name = self.configuration.safe_get('target_helper')
|
self.helper_name = self.configuration.safe_get('iscsi_helper')
|
||||||
self.helper_driver = self.target_mapping[self.helper_name]
|
self.helper_driver = self.target_mapping[self.helper_name]
|
||||||
self.target_driver = importutils.import_object(
|
self.target_driver = importutils.import_object(
|
||||||
self.helper_driver,
|
self.helper_driver,
|
||||||
@ -1024,7 +1087,7 @@ class LinstorIscsiDriver(LinstorBaseDriver):
|
|||||||
# Class with DRBD transport mode
|
# Class with DRBD transport mode
|
||||||
@interface.volumedriver
|
@interface.volumedriver
|
||||||
class LinstorDrbdDriver(LinstorBaseDriver):
|
class LinstorDrbdDriver(LinstorBaseDriver):
|
||||||
"""Cinder DRBD driver that uses Linstor for storage."""
|
"""Cinder DRBD driver that uses LINSTOR for storage."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LinstorDrbdDriver, self).__init__(*args, **kwargs)
|
super(LinstorDrbdDriver, self).__init__(*args, **kwargs)
|
||||||
|
@ -23,9 +23,6 @@ pywbem>=0.7.0 # LGPLv2.1+
|
|||||||
# IBM XIV
|
# IBM XIV
|
||||||
pyxcli>=1.1.5 # Apache-2.0
|
pyxcli>=1.1.5 # Apache-2.0
|
||||||
|
|
||||||
# LINSTOR
|
|
||||||
protobuf>=3.6.1 # BSD
|
|
||||||
|
|
||||||
# RBD
|
# RBD
|
||||||
rados # LGPLv2.1
|
rados # LGPLv2.1
|
||||||
rbd # LGPLv2.1
|
rbd # LGPLv2.1
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The LINSTOR driver for Cinder supports LINSTOR 0.9.12. The driver
|
||||||
|
supports LINSTOR backend using REST API.
|
||||||
|
|
||||||
|
The new driver adds 'linstor_autoplace_count' configuration option that
|
||||||
|
specifies the number of volume replicas.
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The LINSTOR Driver for Cinder now supports LINSTOR 0.9.12.
|
Loading…
x
Reference in New Issue
Block a user