NVMeOF Connection Agent
This is a console script responsible for initiator-side monitoring and self healing of NVMeoF connections and replicated mdraid volumes. It initially supports block storage exposed by KumoScale volume driver used by NVMeoF connector. The functions of the agent are: * Probing of storage orchestrator * Monitor NVMeoF connections and replicated volumes' mdraid states * Report initiator-side connection and volume events to storage orchestrator * Connection and replication state re-conciliation Since this agent relies on methods defined in the NVMeOFConnector "Replicated+" mode, such methods were converted to be usable by the console script. This was done such that the top level privileged methods (for running nvme and mdadm cli tools) are no longer class methods, and are kept in privileged/nvmeof.py All other methods now either use these privileged methods, or run their own rootwrap execute. Implements: Blueprint nvmeof-connection-agent Change-Id: I34e475f9a2a07e19413d40371d62fc809df2105a
This commit is contained in:
parent
98c0916336
commit
b03014b297
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,467 @@
|
|||
# (c) Copyright Kioxia Corporation 2021 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 json
|
||||
|
||||
|
||||
class JsonClass(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(
|
||||
self,
|
||||
default=lambda o: o.__dict__,
|
||||
sort_keys=True,
|
||||
indent=4)
|
||||
|
||||
def __str__(self):
|
||||
return ', '.join(['{key}={value}'.format(
|
||||
key=key, value=self.__dict__.get(key)) for key in self.__dict__])
|
||||
|
||||
def __getattr__(self, item):
|
||||
return "N/A"
|
||||
|
||||
def set_items(self, json_object):
|
||||
json_keys = json_object.keys()
|
||||
for key in json_keys:
|
||||
if not isinstance(json_object[key], 'dict'):
|
||||
self.__dict__[key] = json_object[key]
|
||||
|
||||
|
||||
class ProvisionerResponse(JsonClass):
|
||||
#
|
||||
# Provisioner response data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
prov_entities,
|
||||
res_id=None,
|
||||
status=None,
|
||||
description=None,
|
||||
path=None):
|
||||
JsonClass.__init__(self)
|
||||
self.prov_entities = prov_entities
|
||||
self.resID = res_id
|
||||
self.status = "Success" if status is None else status
|
||||
self.description = self.status if description is None else description
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
items = ""
|
||||
if self.prov_entities:
|
||||
num_of_entities = len(self.prov_entities)
|
||||
if num_of_entities == 1:
|
||||
items = self.prov_entities[0]
|
||||
else:
|
||||
items = num_of_entities
|
||||
return "(" + str(items) + ", " + str(self.resID) + ", " + \
|
||||
str(self.status) + ", " + str(self.description) + ")"
|
||||
|
||||
|
||||
class ProvisionerInfo(JsonClass):
|
||||
#
|
||||
# Provisioner Info data
|
||||
#
|
||||
|
||||
def __init__(self, totalFreeSpace, version, syslogsBackend=None):
|
||||
self.totalFreeSpace = totalFreeSpace
|
||||
self.version = version
|
||||
self.syslogsBackend = syslogsBackend
|
||||
|
||||
|
||||
class Backend(JsonClass):
|
||||
#
|
||||
# Backend data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mgmt_ips=None,
|
||||
rack=None,
|
||||
region=None,
|
||||
zone=None,
|
||||
persistentID=None,
|
||||
inUse=None,
|
||||
hostId=None,
|
||||
state=None,
|
||||
totalCapacity=None,
|
||||
availableCapacity=None,
|
||||
lastProbTime=None,
|
||||
probeInterval=None,
|
||||
totalBW=None,
|
||||
availableBW=None,
|
||||
totalIOPS=None,
|
||||
availableIOPS=None):
|
||||
self.mgmtIPs = mgmt_ips
|
||||
self.rack = rack
|
||||
self.region = region
|
||||
self.zone = zone
|
||||
self.persistentID = persistentID
|
||||
self.inUse = inUse
|
||||
self.state = state
|
||||
self.totalCapacity = totalCapacity
|
||||
self.availableCapacity = availableCapacity
|
||||
self.lastProbTime = lastProbTime
|
||||
self.probeInterval = probeInterval
|
||||
self.totalBW = totalBW
|
||||
self.availableBW = availableBW
|
||||
self.totalIOPS = totalIOPS
|
||||
self.availableIOPS = availableIOPS
|
||||
self.hostId = hostId
|
||||
|
||||
|
||||
class Replica(JsonClass):
|
||||
#
|
||||
# Backend data
|
||||
#
|
||||
|
||||
def __init__(self, sameRackAllowed, racks, regions, zones):
|
||||
self.sameRackAllowed = sameRackAllowed
|
||||
self.racks = racks
|
||||
self.regions = regions
|
||||
self.zones = zones
|
||||
|
||||
|
||||
class Location(JsonClass):
|
||||
#
|
||||
# Location data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
uuid=None,
|
||||
backend=None,
|
||||
replicaState=None,
|
||||
currentStateTime=None):
|
||||
self.uuid = uuid
|
||||
self.backend = backend
|
||||
self.replicaState = replicaState
|
||||
self.currentStateTime = currentStateTime
|
||||
|
||||
|
||||
class VolumeProv(JsonClass):
|
||||
#
|
||||
# Provisioner Volume data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
uuid=None,
|
||||
alias=None,
|
||||
capacity=None,
|
||||
numReplicas=None,
|
||||
maxIOPS=None,
|
||||
desiredIOPS=None,
|
||||
maxBW=None,
|
||||
desiredBW=None,
|
||||
blockSize=None,
|
||||
maxReplicaDownTime=None,
|
||||
snapshotID=None,
|
||||
writable=None,
|
||||
reservedSpace=None,
|
||||
location=None):
|
||||
self.uuid = uuid
|
||||
self.alias = alias
|
||||
self.capacity = capacity
|
||||
self.numReplicas = numReplicas
|
||||
self.maxIOPS = maxIOPS
|
||||
self.desiredIOPS = desiredIOPS
|
||||
self.maxBW = maxBW
|
||||
self.desiredBW = desiredBW
|
||||
self.blockSize = blockSize
|
||||
self.maxReplicaDownTime = maxReplicaDownTime
|
||||
self.snapshotID = snapshotID
|
||||
self.writable = writable
|
||||
self.reservedSpacePercentage = reservedSpace
|
||||
self.location = location
|
||||
|
||||
|
||||
class StorageClass(JsonClass):
|
||||
#
|
||||
# Provisioner Storage Class
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
replicas,
|
||||
racks=None,
|
||||
regions=None,
|
||||
zones=None,
|
||||
blockSize=None,
|
||||
maxIOPSPerGB=None,
|
||||
desiredIOPSPerGB=None,
|
||||
maxBWPerGB=None,
|
||||
desiredBWPerGB=None,
|
||||
sameRackAllowed=None,
|
||||
maxReplicaDownTime=None,
|
||||
hostId=None,
|
||||
spanAllowed=None,
|
||||
name=None,
|
||||
shareSSDBetweenVolumes=None):
|
||||
self.numReplicas = replicas
|
||||
if racks is not None:
|
||||
self.racks = racks
|
||||
if regions is not None:
|
||||
self.regions = regions
|
||||
if zones is not None:
|
||||
self.zones = zones
|
||||
if blockSize is not None:
|
||||
self.blockSize = blockSize
|
||||
if maxIOPSPerGB is not None:
|
||||
self.maxIOPSPerGB = maxIOPSPerGB
|
||||
if desiredIOPSPerGB is not None:
|
||||
self.desiredIOPSPerGB = desiredIOPSPerGB
|
||||
if maxBWPerGB is not None:
|
||||
self.maxBWPerGB = maxBWPerGB
|
||||
if desiredBWPerGB is not None:
|
||||
self.desiredBWPerGB = desiredBWPerGB
|
||||
if sameRackAllowed is not None:
|
||||
self.sameRackAllowed = sameRackAllowed
|
||||
if maxReplicaDownTime is not None:
|
||||
self.maxReplicaDownTime = maxReplicaDownTime
|
||||
if hostId is not None:
|
||||
self.hostId = hostId
|
||||
if spanAllowed is not None:
|
||||
self.allowSpan = spanAllowed
|
||||
if name is not None:
|
||||
self.name = name
|
||||
if shareSSDBetweenVolumes is not None:
|
||||
self.shareSSDBetweenVolumes = shareSSDBetweenVolumes
|
||||
|
||||
|
||||
class VolumeCreate(JsonClass):
|
||||
#
|
||||
# Provisioner Volume data for Create operation
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
alias,
|
||||
capacity,
|
||||
storage_class,
|
||||
prov_type,
|
||||
reserved_space=None,
|
||||
protocol=None,
|
||||
uuid=None):
|
||||
self.alias = alias
|
||||
self.capacity = capacity
|
||||
self.storageClass = storage_class
|
||||
self.provisioningType = prov_type
|
||||
if reserved_space is not None:
|
||||
self.reservedSpacePercentage = reserved_space
|
||||
if protocol is not None:
|
||||
self.protocol = protocol
|
||||
if uuid is not None:
|
||||
self.uuid = uuid
|
||||
|
||||
|
||||
class SyslogEntity(JsonClass):
|
||||
#
|
||||
# Syslog Entity object
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name=None,
|
||||
url=None,
|
||||
state=None,
|
||||
useTls=None,
|
||||
certFileName=None):
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.state = state
|
||||
self.useTls = useTls
|
||||
self.certFileName = certFileName
|
||||
|
||||
|
||||
class SnapshotCreate(JsonClass):
|
||||
#
|
||||
# Provisioner Snapshot data for Create operation
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
alias,
|
||||
volumeID,
|
||||
reservedSpacePercentage=None,
|
||||
snapshotID=None):
|
||||
self.alias = alias
|
||||
self.volumeID = volumeID
|
||||
if reservedSpacePercentage is not None:
|
||||
self.reservedSpacePercentage = reservedSpacePercentage
|
||||
if snapshotID is not None:
|
||||
self.snapshotID = snapshotID
|
||||
|
||||
|
||||
class SnapshotEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Snapshot Entity data for Show operation
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
alias=None,
|
||||
snapshotID=None,
|
||||
reservedSpace=None,
|
||||
volumeID=None,
|
||||
capacity=None,
|
||||
timestamp=None):
|
||||
self.alias = alias
|
||||
self.volumeID = volumeID
|
||||
self.reservedSpace = reservedSpace
|
||||
self.snapshotID = snapshotID
|
||||
self.capacity = capacity
|
||||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class SnapshotVolumeCreate(JsonClass):
|
||||
#
|
||||
# Provisioner Snapshot Volume data for Create operation
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
alias,
|
||||
snapshotID,
|
||||
writable,
|
||||
reservedSpacePercentage=None,
|
||||
volumeID=None,
|
||||
maxIOPSPerGB=None,
|
||||
maxBWPerGB=None,
|
||||
protocol=None,
|
||||
spanAllowed=None,
|
||||
storageClassName=None):
|
||||
self.alias = alias
|
||||
self.snapshotID = snapshotID
|
||||
self.writable = writable
|
||||
if reservedSpacePercentage is not None:
|
||||
self.reservedSpacePercentage = reservedSpacePercentage
|
||||
if volumeID is not None:
|
||||
self.volumeID = volumeID
|
||||
if maxIOPSPerGB is not None:
|
||||
self.maxIOPSPerGB = maxIOPSPerGB
|
||||
if maxBWPerGB is not None:
|
||||
self.maxBWPerGB = maxBWPerGB
|
||||
if protocol is not None:
|
||||
self.protocol = protocol
|
||||
if spanAllowed is not None:
|
||||
self.allowSpan = spanAllowed
|
||||
if storageClassName is not None:
|
||||
self.storageClassName = storageClassName
|
||||
|
||||
|
||||
class ForwardEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Forward Entity data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
loggingType,
|
||||
level,
|
||||
host,
|
||||
appName,
|
||||
message,
|
||||
parametersList):
|
||||
self.loggingType = loggingType
|
||||
self.level = level
|
||||
self.host = host
|
||||
self.appName = appName
|
||||
self.message = message
|
||||
self.parametersList = parametersList
|
||||
|
||||
|
||||
class LicenseEntity(JsonClass):
|
||||
#
|
||||
# Provisioner License Entity data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
license_type=None,
|
||||
expirationDate=None,
|
||||
maxBackends=None):
|
||||
self.type = license_type
|
||||
self.expirationDate = expirationDate
|
||||
self.maxBackends = maxBackends
|
||||
|
||||
|
||||
class HostEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Host Entity data
|
||||
#
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
nqn=None,
|
||||
uuid=None,
|
||||
name=None,
|
||||
clientType=None,
|
||||
version=None,
|
||||
state=None,
|
||||
lastProbeTime=None,
|
||||
duration=None):
|
||||
self.nqn = nqn
|
||||
self.uuid = uuid
|
||||
self.name = name
|
||||
self.clientType = clientType
|
||||
self.version = version
|
||||
self.state = state
|
||||
self.lastProbeTime = lastProbeTime
|
||||
self.duration = duration
|
||||
|
||||
|
||||
class TargetEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Target Entity data for Show operation
|
||||
#
|
||||
|
||||
def __init__(self, alias=None):
|
||||
self.alias = alias
|
||||
|
||||
|
||||
class TenantEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Tenant Entity data for Show operation
|
||||
#
|
||||
|
||||
def __init__(self, capacity, iops, bw, uuid=None, name=None):
|
||||
self.capacity = capacity
|
||||
self.totalIOPS = iops
|
||||
self.totalBW = bw
|
||||
if uuid is not None:
|
||||
self.tenantId = uuid
|
||||
if name is not None:
|
||||
self.name = name
|
||||
|
||||
|
||||
class CloneEntity(JsonClass):
|
||||
#
|
||||
# Provisioner Clone Entity data
|
||||
#
|
||||
|
||||
def __init__(self, sourceVolumeId, alias, volumeId=None,
|
||||
reservedSpacePercentage=None,
|
||||
capacity=None):
|
||||
self.sourceVolumeId = sourceVolumeId
|
||||
self.alias = alias
|
||||
if volumeId is not None:
|
||||
self.volumeId = volumeId
|
||||
if reservedSpacePercentage is not None:
|
||||
self.reservedSpacePercentage = reservedSpacePercentage
|
||||
if capacity is not None:
|
||||
self.capacity = capacity
|
File diff suppressed because it is too large
Load Diff
|
@ -24,10 +24,6 @@ from oslo_log import log as logging
|
|||
from os_brick import exception
|
||||
from os_brick.i18n import _
|
||||
from os_brick.initiator.connectors import base
|
||||
try:
|
||||
from os_brick.initiator.connectors import nvmeof_agent
|
||||
except ImportError:
|
||||
nvmeof_agent = None
|
||||
from os_brick.privileged import nvmeof as priv_nvme
|
||||
from os_brick.privileged import rootwrap as priv_rootwrap
|
||||
from os_brick import utils
|
||||
|
@ -63,19 +59,20 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
if device_path:
|
||||
return [device_path]
|
||||
volume_replicas = connection_properties.get('volume_replicas')
|
||||
replica_count = connection_properties.get('replica_count')
|
||||
if not volume_replicas: # compatibility
|
||||
return []
|
||||
|
||||
try:
|
||||
if volume_replicas and len(volume_replicas) > 1:
|
||||
if volume_replicas and replica_count > 1:
|
||||
return ['/dev/md/' + connection_properties.get('alias')]
|
||||
if volume_replicas and len(volume_replicas) == 1:
|
||||
if volume_replicas and replica_count == 1:
|
||||
return [NVMeOFConnector.get_nvme_device_path(
|
||||
self, volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['vol_uuid'])]
|
||||
else:
|
||||
return [NVMeOFConnector.get_nvme_device_path(
|
||||
self, connection_properties.get('target_nqn'),
|
||||
connection_properties.get('target_nqn'),
|
||||
connection_properties.get('vol_uuid'))]
|
||||
except exception.VolumeDeviceNotFound:
|
||||
return []
|
||||
|
@ -101,7 +98,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
ret = {}
|
||||
|
||||
nqn = None
|
||||
uuid = nvmf._get_host_uuid()
|
||||
uuid = priv_nvme.get_host_uuid()
|
||||
suuid = nvmf._get_system_uuid()
|
||||
if cls.nvme_present():
|
||||
nqn = nvmf._get_host_nqn()
|
||||
|
@ -113,21 +110,6 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
ret['nqn'] = nqn
|
||||
return ret
|
||||
|
||||
def _get_host_uuid(self):
|
||||
cmd = ('findmnt', '/', '-n', '-o', 'SOURCE')
|
||||
try:
|
||||
lines, err = self._execute(
|
||||
*cmd, run_as_root=True, root_helper=self._root_helper)
|
||||
blkid_cmd = (
|
||||
'blkid', lines.split('\n')[0], '-s', 'UUID', '-o', 'value')
|
||||
lines, _err = self._execute(
|
||||
*blkid_cmd, run_as_root=True, root_helper=self._root_helper)
|
||||
return lines.split('\n')[0]
|
||||
except putils.ProcessExecutionError as e:
|
||||
LOG.warning(
|
||||
"Process execution error in _get_host_uuid: %s" % str(e))
|
||||
return None
|
||||
|
||||
def _get_host_nqn(self):
|
||||
try:
|
||||
with open('/etc/nvme/hostnqn', 'r') as f:
|
||||
|
@ -424,6 +406,10 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
{'props': connection_properties})
|
||||
raise exception.VolumePathsNotFound()
|
||||
|
||||
#
|
||||
# "Replicated+" NVMeOF Connector methods below
|
||||
#
|
||||
|
||||
@utils.trace
|
||||
def _connect_volume_replicated(self, connection_properties):
|
||||
"""connect to volume on host
|
||||
|
@ -435,6 +421,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
"""
|
||||
|
||||
volume_replicas = connection_properties.get('volume_replicas')
|
||||
replica_count = connection_properties.get('replica_count')
|
||||
volume_alias = connection_properties.get('alias')
|
||||
|
||||
if volume_replicas:
|
||||
|
@ -453,9 +440,9 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
raise exception.VolumeDeviceNotFound(
|
||||
device=volume_replicas)
|
||||
|
||||
if len(volume_replicas) > 1:
|
||||
if replica_count > 1:
|
||||
device_path = self._handle_replicated_volume(
|
||||
host_device_paths, volume_alias, len(volume_replicas))
|
||||
host_device_paths, volume_alias, replica_count)
|
||||
else:
|
||||
device_path = self._handle_single_replica(
|
||||
host_device_paths, volume_alias)
|
||||
|
@ -465,9 +452,6 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
connection_properties['vol_uuid'],
|
||||
connection_properties['portals'])
|
||||
|
||||
if nvmeof_agent:
|
||||
nvmeof_agent.NVMeOFAgent.ensure_running(self)
|
||||
|
||||
return {'type': 'block', 'path': device_path}
|
||||
|
||||
@utils.trace
|
||||
|
@ -483,18 +467,19 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
device_path = '/dev/md/' + connection_properties['alias']
|
||||
|
||||
if volume_replicas and len(volume_replicas) > 1:
|
||||
NVMeOFConnector.end_raid(self, device_path)
|
||||
NVMeOFConnector.end_raid(device_path)
|
||||
else:
|
||||
if self._get_fs_type(device_path) == 'linux_raid_member':
|
||||
NVMeOFConnector.end_raid(self, device_path)
|
||||
if (NVMeOFConnector._get_fs_type(device_path)
|
||||
== 'linux_raid_member'):
|
||||
NVMeOFConnector.end_raid(device_path)
|
||||
|
||||
def _extend_volume_replicated(self, connection_properties):
|
||||
volume_replicas = connection_properties.get('volume_replicas')
|
||||
|
||||
if volume_replicas and len(volume_replicas) > 1:
|
||||
device_path = '/dev/md/' + connection_properties['alias']
|
||||
NVMeOFConnector.run_mdadm(
|
||||
self, ['mdadm', '--grow', '--size', 'max', device_path])
|
||||
priv_nvme.run_mdadm(
|
||||
['mdadm', '--grow', '--size', 'max', device_path])
|
||||
else:
|
||||
if not volume_replicas:
|
||||
target_nqn = connection_properties['target_nqn']
|
||||
|
@ -503,20 +488,20 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
target_nqn = volume_replicas[0]['target_nqn']
|
||||
vol_uuid = volume_replicas[0]['vol_uuid']
|
||||
device_path = NVMeOFConnector.get_nvme_device_path(
|
||||
self, target_nqn, vol_uuid)
|
||||
target_nqn, vol_uuid)
|
||||
|
||||
return self._linuxscsi.get_device_size(device_path)
|
||||
|
||||
def _connect_target_volume(self, target_nqn, vol_uuid, portals):
|
||||
try:
|
||||
host_device_path = NVMeOFConnector.get_nvme_device_path(
|
||||
self, target_nqn, vol_uuid)
|
||||
target_nqn, vol_uuid)
|
||||
except exception.VolumeDeviceNotFound:
|
||||
host_device_path = None
|
||||
|
||||
if not host_device_path:
|
||||
any_connect = NVMeOFConnector.connect_to_portals(
|
||||
self, target_nqn, portals)
|
||||
target_nqn, portals)
|
||||
if not any_connect:
|
||||
LOG.error(
|
||||
"No successful connections: %(host_devices)s",
|
||||
|
@ -524,21 +509,57 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
raise exception.VolumeDeviceNotFound(device=target_nqn)
|
||||
|
||||
host_device_path = NVMeOFConnector.get_nvme_device_path(
|
||||
self, target_nqn, vol_uuid)
|
||||
target_nqn, vol_uuid)
|
||||
if not host_device_path:
|
||||
LOG.error(
|
||||
"No accessible volume device: %(host_devices)s",
|
||||
{'host_devices': target_nqn})
|
||||
raise exception.VolumeDeviceNotFound(device=target_nqn)
|
||||
else:
|
||||
NVMeOFConnector.rescan(self, target_nqn, vol_uuid)
|
||||
NVMeOFConnector.rescan(target_nqn, vol_uuid)
|
||||
host_device_path = NVMeOFConnector.get_nvme_device_path(
|
||||
self, target_nqn, vol_uuid)
|
||||
target_nqn, vol_uuid)
|
||||
|
||||
return host_device_path
|
||||
|
||||
def _handle_replicated_volume(self, host_device_paths,
|
||||
volume_alias, num_of_replicas):
|
||||
path_in_raid = False
|
||||
for dev_path in host_device_paths:
|
||||
path_in_raid = NVMeOFConnector._is_device_in_raid(dev_path)
|
||||
if path_in_raid:
|
||||
break
|
||||
device_path = '/dev/md/' + volume_alias
|
||||
if path_in_raid:
|
||||
NVMeOFConnector.stop_and_assemble_raid(
|
||||
host_device_paths, device_path, False)
|
||||
else:
|
||||
paths_found = len(host_device_paths)
|
||||
if num_of_replicas > paths_found:
|
||||
LOG.error(
|
||||
'Cannot create MD as %s out of %s legs were found.',
|
||||
paths_found, num_of_replicas)
|
||||
raise exception.VolumeDeviceNotFound(device=volume_alias)
|
||||
NVMeOFConnector.create_raid(host_device_paths, '1',
|
||||
volume_alias, volume_alias, False)
|
||||
|
||||
return device_path
|
||||
|
||||
def _handle_single_replica(self, host_device_paths, volume_alias):
|
||||
if (NVMeOFConnector._get_fs_type(host_device_paths[0])
|
||||
== 'linux_raid_member'):
|
||||
md_path = '/dev/md/' + volume_alias
|
||||
NVMeOFConnector.stop_and_assemble_raid(
|
||||
host_device_paths, md_path, False)
|
||||
return md_path
|
||||
return host_device_paths[0]
|
||||
|
||||
#
|
||||
# Static methods below are also used by NVMeOF-Agent
|
||||
#
|
||||
|
||||
@staticmethod
|
||||
def connect_to_portals(executor, target_nqn, target_portals):
|
||||
def connect_to_portals(target_nqn, target_portals):
|
||||
"""connect to any of NVMe-oF target portals"""
|
||||
any_connect = False
|
||||
for portal in target_portals:
|
||||
|
@ -552,7 +573,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
'connect', '-a', portal_address, '-s', portal_port, '-t',
|
||||
portal_transport, '-n', target_nqn, '-Q', '128', '-l', '-1')
|
||||
try:
|
||||
NVMeOFConnector.run_nvme_cli(executor, nvme_command)
|
||||
priv_nvme.run_nvme_cli(nvme_command)
|
||||
any_connect = True
|
||||
break
|
||||
except Exception:
|
||||
|
@ -560,18 +581,16 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return any_connect
|
||||
|
||||
@staticmethod
|
||||
def _get_nvme_controller(executor, target_nqn):
|
||||
def get_nvme_controller(target_nqn):
|
||||
ctrls = glob.glob('/sys/class/nvme-fabrics/ctl/nvme*')
|
||||
for ctrl in ctrls:
|
||||
try:
|
||||
lines, _err = executor._execute(
|
||||
'cat', ctrl + '/subsysnqn', run_as_root=True,
|
||||
root_helper=executor._root_helper)
|
||||
lines, _err = priv_rootwrap.custom_execute(
|
||||
'cat', ctrl + '/subsysnqn')
|
||||
for line in lines.split('\n'):
|
||||
if line == target_nqn:
|
||||
state, _err = executor._execute(
|
||||
'cat', ctrl + '/state', run_as_root=True,
|
||||
root_helper=executor._root_helper)
|
||||
state, _err = priv_rootwrap.custom_execute(
|
||||
'cat', ctrl + '/state')
|
||||
if 'live' not in state:
|
||||
LOG.debug("nvmeof ctrl device not live: %s", ctrl)
|
||||
raise exception.VolumeDeviceNotFound(device=ctrl)
|
||||
|
@ -583,76 +602,30 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
|
||||
@staticmethod
|
||||
@utils.retry(exceptions=exception.VolumeDeviceNotFound)
|
||||
def get_nvme_device_path(executor, target_nqn, vol_uuid):
|
||||
nvme_ctrl = NVMeOFConnector._get_nvme_controller(executor, target_nqn)
|
||||
try:
|
||||
blocks = glob.glob(
|
||||
'/sys/class/nvme-fabrics/ctl/' + nvme_ctrl +
|
||||
'/' + nvme_ctrl + 'n*')
|
||||
for block in blocks:
|
||||
uuid_lines, _err = executor._execute(
|
||||
'cat', block + '/uuid', run_as_root=True,
|
||||
root_helper=executor._root_helper)
|
||||
if uuid_lines.split('\n')[0] == vol_uuid:
|
||||
return '/dev/' + block[block.rfind('/') + 1:]
|
||||
except putils.ProcessExecutionError as e:
|
||||
LOG.exception(e)
|
||||
|
||||
def get_nvme_device_path(target_nqn, vol_uuid):
|
||||
nvme_ctrl = NVMeOFConnector.get_nvme_controller(target_nqn)
|
||||
blocks = glob.glob(
|
||||
'/sys/class/block/' + nvme_ctrl + 'n*')
|
||||
for block in blocks:
|
||||
uuid_path = block + '/uuid'
|
||||
try:
|
||||
exists = os.path.exists(uuid_path)
|
||||
if exists:
|
||||
uuid_lines, _err = priv_rootwrap.custom_execute(
|
||||
'cat', uuid_path)
|
||||
if uuid_lines.split('\n')[0] == vol_uuid:
|
||||
return '/dev/' + block[block.rfind('/') + 1:]
|
||||
except putils.ProcessExecutionError as e:
|
||||
LOG.exception(e)
|
||||
raise exception.VolumeDeviceNotFound(device=vol_uuid)
|
||||
|
||||
def _handle_replicated_volume(self, host_device_paths,
|
||||
volume_alias, num_of_replicas):
|
||||
path_in_raid = False
|
||||
for dev_path in host_device_paths:
|
||||
path_in_raid = NVMeOFConnector._is_device_in_raid(self, dev_path)
|
||||
if path_in_raid:
|
||||
break
|
||||
device_path = '/dev/md/' + volume_alias
|
||||
if path_in_raid:
|
||||
NVMeOFConnector.stop_and_assemble_raid(
|
||||
self, host_device_paths, device_path, False)
|
||||
else:
|
||||
paths_found = len(host_device_paths)
|
||||
if num_of_replicas > paths_found:
|
||||
LOG.error(
|
||||
'Cannot create MD as %s out of %s legs were found.',
|
||||
paths_found, num_of_replicas)
|
||||
raise exception.VolumeDeviceNotFound(device=volume_alias)
|
||||
NVMeOFConnector.create_raid(self, host_device_paths, '1',
|
||||
volume_alias, volume_alias, False)
|
||||
|
||||
return device_path
|
||||
|
||||
def _handle_single_replica(self, host_device_paths, volume_alias):
|
||||
if self._get_fs_type(host_device_paths[0]) == 'linux_raid_member':
|
||||
md_path = '/dev/md/' + volume_alias
|
||||
NVMeOFConnector.stop_and_assemble_raid(
|
||||
self, host_device_paths, md_path, False)
|
||||
return md_path
|
||||
return host_device_paths[0]
|
||||
|
||||
@staticmethod
|
||||
def run_mdadm(executor, cmd, raise_exception=False):
|
||||
cmd_output = None
|
||||
try:
|
||||
lines, err = executor._execute(
|
||||
*cmd, run_as_root=True, root_helper=executor._root_helper)
|
||||
for line in lines.split('\n'):
|
||||
cmd_output = line
|
||||
break
|
||||
except putils.ProcessExecutionError as ex:
|
||||
LOG.warning("[!] Could not run mdadm: %s", str(ex))
|
||||
if raise_exception:
|
||||
raise ex
|
||||
return cmd_output
|
||||
|
||||
@staticmethod
|
||||
def _is_device_in_raid(self, device_path):
|
||||
def _is_device_in_raid(device_path):
|
||||
cmd = ['mdadm', '--examine', device_path]
|
||||
raid_expected = device_path + ':'
|
||||
|
||||
try:
|
||||
lines, err = self._execute(
|
||||
*cmd, run_as_root=True, root_helper=self._root_helper)
|
||||
lines, err = priv_nvme.run_mdadm(cmd)
|
||||
for line in lines.split('\n'):
|
||||
if line == raid_expected:
|
||||
return True
|
||||
|
@ -669,7 +642,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return ''
|
||||
|
||||
@staticmethod
|
||||
def get_md_name(executor, device_name):
|
||||
def get_md_name(device_name):
|
||||
get_md_cmd = (
|
||||
'cat /proc/mdstat | grep ' + device_name +
|
||||
' | awk \'{print $1;}\'')
|
||||
|
@ -678,8 +651,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
cmd_output = None
|
||||
|
||||
try:
|
||||
lines, err = executor._execute(
|
||||
*cmd, run_as_root=True, root_helper=executor._root_helper)
|
||||
lines, err = priv_rootwrap.custom_execute(*cmd)
|
||||
|
||||
for line in lines.split('\n'):
|
||||
cmd_output = line
|
||||
|
@ -695,7 +667,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return None
|
||||
|
||||
@staticmethod
|
||||
def stop_and_assemble_raid(executor, drives, md_path, read_only):
|
||||
def stop_and_assemble_raid(drives, md_path, read_only):
|
||||
md_name = None
|
||||
i = 0
|
||||
assembled = False
|
||||
|
@ -703,7 +675,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
while i < 5 and not assembled:
|
||||
for drive in drives:
|
||||
device_name = drive[5:]
|
||||
md_name = NVMeOFConnector.get_md_name(executor, device_name)
|
||||
md_name = NVMeOFConnector.get_md_name(device_name)
|
||||
link = NVMeOFConnector.ks_readlink(md_path)
|
||||
if link != '':
|
||||
link = os.path.basename(link)
|
||||
|
@ -715,16 +687,16 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
time.sleep(1)
|
||||
|
||||
if md_name and md_name != link:
|
||||
NVMeOFConnector.stop_raid(executor, md_name)
|
||||
NVMeOFConnector.stop_raid(md_name)
|
||||
|
||||
try:
|
||||
assembled = NVMeOFConnector.assemble_raid(
|
||||
executor, drives, md_path, read_only)
|
||||
drives, md_path, read_only)
|
||||
except Exception:
|
||||
i += 1
|
||||
|
||||
@staticmethod
|
||||
def assemble_raid(executor, drives, md_path, read_only):
|
||||
def assemble_raid(drives, md_path, read_only):
|
||||
cmd = ['mdadm', '--assemble', '--run', md_path]
|
||||
|
||||
if read_only:
|
||||
|
@ -734,7 +706,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
cmd.append(drives[i])
|
||||
|
||||
try:
|
||||
NVMeOFConnector.run_mdadm(executor, cmd, True)
|
||||
priv_nvme.run_mdadm(cmd, True)
|
||||
except putils.ProcessExecutionError as ex:
|
||||
LOG.warning("[!] Could not _assemble_raid: %s", str(ex))
|
||||
raise ex
|
||||
|
@ -742,7 +714,7 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return True
|
||||
|
||||
@staticmethod
|
||||
def create_raid(executor, drives, raid_type, device_name, name, read_only):
|
||||
def create_raid(drives, raid_type, device_name, name, read_only):
|
||||
cmd = ['mdadm']
|
||||
num_drives = len(drives)
|
||||
cmd.append('-C')
|
||||
|
@ -769,16 +741,16 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
cmd.append(drives[i])
|
||||
|
||||
LOG.debug('[!] cmd = ' + str(cmd))
|
||||
NVMeOFConnector.run_mdadm(executor, cmd)
|
||||
priv_nvme.run_mdadm(cmd)
|
||||
|
||||
@staticmethod
|
||||
def end_raid(executor, device_path):
|
||||
raid_exists = NVMeOFConnector.is_raid_exists(executor, device_path)
|
||||
def end_raid(device_path):
|
||||
raid_exists = NVMeOFConnector.is_raid_exists(device_path)
|
||||
if raid_exists:
|
||||
for i in range(10):
|
||||
try:
|
||||
cmd_out = NVMeOFConnector.stop_raid(
|
||||
executor, device_path)
|
||||
device_path)
|
||||
if not cmd_out:
|
||||
break
|
||||
except Exception:
|
||||
|
@ -788,26 +760,25 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
is_exist = os.path.exists(device_path)
|
||||
LOG.debug("[!] is_exist = %s", is_exist)
|
||||
if is_exist:
|
||||
NVMeOFConnector.remove_raid(executor, device_path)
|
||||
NVMeOFConnector.remove_raid(device_path)
|
||||
os.remove(device_path)
|
||||
except Exception:
|
||||
LOG.debug('[!] Exception_stop_raid!')
|
||||
|
||||
@staticmethod
|
||||
def stop_raid(executor, md_path):
|
||||
def stop_raid(md_path):
|
||||
cmd = ['mdadm', '--stop', md_path]
|
||||
LOG.debug("[!] cmd = " + str(cmd))
|
||||
cmd_out = NVMeOFConnector.run_mdadm(executor, cmd)
|
||||
return cmd_out
|
||||
lines, err = priv_nvme.run_mdadm(cmd)
|
||||
return lines.split('\n')[0]
|
||||
|
||||
@staticmethod
|
||||
def is_raid_exists(executor, device_path):
|
||||
def is_raid_exists(device_path):
|
||||
cmd = ['mdadm', '--detail', device_path]
|
||||
LOG.debug("[!] cmd = " + str(cmd))
|
||||
raid_expected = device_path + ':'
|
||||
try:
|
||||
lines, err = executor._execute(
|
||||
*cmd, run_as_root=True, root_helper=executor._root_helper)
|
||||
lines, err = priv_nvme.run_mdadm(cmd)
|
||||
|
||||
for line in lines.split('\n'):
|
||||
LOG.debug("[!] line = " + line)
|
||||
|
@ -819,41 +790,30 @@ class NVMeOFConnector(base.BaseLinuxConnector):
|
|||
return False
|
||||
|
||||
@staticmethod
|
||||
def remove_raid(executor, device_path):
|
||||
def remove_raid(device_path):
|
||||
cmd = ['mdadm', '--remove', device_path]
|
||||
LOG.debug("[!] cmd = " + str(cmd))
|
||||
NVMeOFConnector.run_mdadm(executor, cmd)
|
||||
priv_nvme.run_mdadm(cmd)
|
||||
|
||||
@staticmethod
|
||||
def run_nvme_cli(executor, nvme_command, **kwargs):
|
||||
(out, err) = executor._execute('nvme', *nvme_command, run_as_root=True,
|
||||
root_helper=executor._root_helper,
|
||||
check_exit_code=True)
|
||||
msg = ("nvme %(nvme_command)s: stdout=%(out)s stderr=%(err)s" %
|
||||
{'nvme_command': nvme_command, 'out': out, 'err': err})
|
||||
LOG.debug("[!] " + msg)
|
||||
|
||||
return out, err
|
||||
|
||||
@staticmethod
|
||||
def rescan(executor, target_nqn, vol_uuid):
|
||||
def rescan(target_nqn, vol_uuid):
|
||||
ctr_device = (
|
||||
NVMeOFConnector.get_search_path() +
|
||||
NVMeOFConnector._get_nvme_controller(executor, target_nqn))
|
||||
NVMeOFConnector.get_nvme_controller(target_nqn))
|
||||
nvme_command = ('ns-rescan', ctr_device)
|
||||
try:
|
||||
NVMeOFConnector.run_nvme_cli(executor, nvme_command)
|
||||
priv_nvme.run_nvme_cli(nvme_command)
|
||||
except Exception as e:
|
||||
raise exception.CommandExecutionFailed(e, cmd=nvme_command)
|
||||
|
||||
def _get_fs_type(self, device_path):
|
||||
@staticmethod
|
||||
def _get_fs_type(device_path):
|
||||
cmd = ['blkid', device_path, '-s', 'TYPE', '-o', 'value']
|
||||
LOG.debug("[!] cmd = " + str(cmd))
|
||||
fs_type = None
|
||||
|
||||
try:
|
||||
lines, err = self._execute(
|
||||
*cmd, run_as_root=True, root_helper=self._root_helper)
|
||||
lines, err = priv_rootwrap.custom_execute(*cmd)
|
||||
|
||||
fs_type = lines.split('\n')[0]
|
||||
except putils.ProcessExecutionError:
|
||||
|
|
|
@ -64,3 +64,36 @@ def create_hostnqn():
|
|||
LOG.warning("Could not generate host nqn: %s" % str(e))
|
||||
|
||||
return host_nqn
|
||||
|
||||
|
||||
@os_brick.privileged.default.entrypoint
|
||||
def get_host_uuid():
|
||||
try:
|
||||
lines, _err = rootwrap.custom_execute(
|
||||
'findmnt', '/', '-n', '-o', 'SOURCE')
|
||||
lines, _err = rootwrap.custom_execute(
|
||||
'blkid', lines.split('\n')[0], '-s', 'UUID', '-o', 'value')
|
||||
return lines.split('\n')[0]
|
||||
except putils.ProcessExecutionError as e:
|
||||
LOG.warning(
|
||||
"Could not get host uuid: %s" % str(e))
|
||||
return None
|
||||
|
||||
|
||||
@os_brick.privileged.default.entrypoint
|
||||
def run_nvme_cli(nvme_command):
|
||||
out, err = rootwrap.custom_execute(
|
||||
'nvme', *nvme_command, check_exit_code=True)
|
||||
LOG.debug("nvme %(nvme_command)s: stdout=%(out)s stderr=%(err)s" %
|
||||
{'nvme_command': nvme_command, 'out': out, 'err': err})
|
||||
return out, err
|
||||
|
||||
|
||||
@os_brick.privileged.default.entrypoint
|
||||
def run_mdadm(cmd, raise_exception=False):
|
||||
try:
|
||||
return rootwrap.custom_execute(*cmd)
|
||||
except putils.ProcessExecutionError as ex:
|
||||
LOG.warning("Could not run mdadm: %s - %s", str(cmd), str(ex))
|
||||
if raise_exception:
|
||||
raise ex
|
||||
|
|
|
@ -19,7 +19,6 @@ import ddt
|
|||
from oslo_concurrency import processutils as putils
|
||||
|
||||
from os_brick import exception
|
||||
from os_brick import executor
|
||||
from os_brick.initiator.connectors import nvmeof
|
||||
from os_brick.initiator import linuxscsi
|
||||
from os_brick.privileged import rootwrap as priv_rootwrap
|
||||
|
@ -27,7 +26,6 @@ from os_brick.tests.initiator import test_connector
|
|||
|
||||
|
||||
TARGET_NQN = 'target.nqn'
|
||||
EXECUTOR = executor.Executor(None)
|
||||
VOL_UUID = 'c20aba21-6ef6-446b-b374-45733b4883ba'
|
||||
NVME_DEVICE_PATH = '/dev/nvme1'
|
||||
NVME_NS_PATH = '/dev/nvme1n1'
|
||||
|
@ -44,6 +42,7 @@ volume_replicas = [{'target_nqn': 'fakenqn1', 'vol_uuid': 'fakeuuid1',
|
|||
connection_properties = {
|
||||
'alias': 'fakealias',
|
||||
'vol_uuid': 'fakevoluuid',
|
||||
'replica_count': 3,
|
||||
'volume_replicas': volume_replicas
|
||||
}
|
||||
fake_portal = ('fake', 'portal', 'tcp')
|
||||
|
@ -75,28 +74,13 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
log.assert_called_once()
|
||||
self.assertFalse(nvme_present)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_execute', autospec=True)
|
||||
def test_get_sysuuid_without_newline(self, mock_execute):
|
||||
mock_execute.return_value = (
|
||||
"9126E942-396D-11E7-B0B7-A81E84C186D1\n", "")
|
||||
uuid = self.connector._get_host_uuid()
|
||||
expected_uuid = "9126E942-396D-11E7-B0B7-A81E84C186D1"
|
||||
self.assertEqual(expected_uuid, uuid)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_execute', autospec=True)
|
||||
def test_get_sysuuid_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
uuid = self.connector._get_host_uuid()
|
||||
self.assertIsNone(uuid)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'nvme_present',
|
||||
return_value=True)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_nqn',
|
||||
return_value='fakenqn')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
|
||||
return_value=None)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid',
|
||||
return_value=None)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'get_host_uuid', return_value = None)
|
||||
def test_get_connector_properties_without_sysuuid(self, mock_host_uuid,
|
||||
mock_sysuuid, mock_nqn,
|
||||
mock_nvme_present):
|
||||
|
@ -108,7 +92,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_nqn', autospec=True)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
|
||||
autospec=True)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid', autospec=True)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'get_host_uuid')
|
||||
def test_get_connector_properties_with_sysuuid(self, mock_host_uuid,
|
||||
mock_sysuuid, mock_nqn,
|
||||
mock_nvme_present):
|
||||
|
@ -131,13 +115,14 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_get_device_path.return_value = '/dev/nvme1n1'
|
||||
connection_properties = {
|
||||
'alias': 'fakealias',
|
||||
'replica_count': 1,
|
||||
'volume_replicas': [volume_replicas[0]]
|
||||
}
|
||||
self.assertEqual(self.connector.get_volume_paths(
|
||||
connection_properties),
|
||||
['/dev/nvme1n1'])
|
||||
mock_get_device_path.assert_called_with(
|
||||
self.connector, volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['vol_uuid'])
|
||||
|
||||
def test_get_volume_paths_replicated(self):
|
||||
|
@ -159,6 +144,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
connection_properties1 = {
|
||||
'target_nqn': 'fakenqn',
|
||||
'vol_uuid': 'fakeuuid',
|
||||
'replica_count': 1,
|
||||
'volume_replicas': [volume_replicas[0]]
|
||||
}
|
||||
mock_connect_target_volume.return_value = '/dev/nvme0n1'
|
||||
|
@ -325,8 +311,10 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
root_helper=None,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'is_raid_exists')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_fs_type')
|
||||
def test_disconnect_unreplicated_volume_nova(self, mock_get_fs_type):
|
||||
def test_disconnect_unreplicated_volume_nova(self, mock_get_fs_type,
|
||||
mock_raidexist):
|
||||
connection_properties = {
|
||||
'vol_uuid': 'fakeuuid',
|
||||
'portals': [('10.0.0.1', 4420, 'tcp')],
|
||||
|
@ -345,7 +333,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_end_raid.return_value = None
|
||||
self.connector.disconnect_volume(connection_properties, None)
|
||||
device_path = '/dev/md/' + connection_properties['alias']
|
||||
mock_end_raid.assert_called_with(self.connector, device_path)
|
||||
mock_end_raid.assert_called_with(device_path)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'end_raid')
|
||||
def test_disconnect_replicated_volume_nova(self, mock_end_raid):
|
||||
|
@ -355,7 +343,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
'device_path': '/dev/md/md1'
|
||||
}
|
||||
self.connector.disconnect_volume(connection_properties, None)
|
||||
mock_end_raid.assert_called_with(self.connector, '/dev/md/md1')
|
||||
mock_end_raid.assert_called_with('/dev/md/md1')
|
||||
|
||||
def test_disconnect_unreplicated_volume_cinder(self):
|
||||
connection_properties = {
|
||||
|
@ -373,7 +361,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector.disconnect_volume(connection_properties,
|
||||
device_info,
|
||||
ignore_errors=True)
|
||||
mock_end_raid.assert_called_with(self.connector, '/dev/md/md1')
|
||||
mock_end_raid.assert_called_with('/dev/md/md1')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_device_path')
|
||||
@mock.patch.object(linuxscsi.LinuxSCSI, 'get_device_size')
|
||||
|
@ -390,7 +378,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector.extend_volume(connection_properties),
|
||||
100)
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['target_nqn'],
|
||||
volume_replicas[0]['vol_uuid'])
|
||||
mock_device_size.assert_called_with('/dev/nvme0n1')
|
||||
|
||||
|
@ -408,10 +396,10 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector._extend_volume_replicated(
|
||||
connection_properties), 100)
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, 'fakenqn', 'fakeuuid')
|
||||
'fakenqn', 'fakeuuid')
|
||||
mock_device_size.assert_called_with('/dev/nvme0n1')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
@mock.patch.object(linuxscsi.LinuxSCSI, 'get_device_size')
|
||||
def test_extend_volume_replicated(
|
||||
self, mock_device_size, mock_mdadm):
|
||||
|
@ -421,7 +409,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
100)
|
||||
device_path = '/dev/md/' + connection_properties['alias']
|
||||
mock_mdadm.assert_called_with(
|
||||
self.connector, ['mdadm', '--grow', '--size', 'max', device_path])
|
||||
['mdadm', '--grow', '--size', 'max', device_path])
|
||||
mock_device_size.assert_called_with(device_path)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'rescan')
|
||||
|
@ -434,7 +422,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
'fakenqn', 'fakeuuid', [('fake', 'portal', 'tcp')]),
|
||||
'/dev/nvme0n1')
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, 'fakenqn', 'fakeuuid')
|
||||
'fakenqn', 'fakeuuid')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'connect_to_portals')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_device_path')
|
||||
|
@ -446,7 +434,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector._connect_target_volume, TARGET_NQN,
|
||||
VOL_UUID, [('fake', 'portal', 'tcp')])
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, TARGET_NQN, VOL_UUID)
|
||||
TARGET_NQN, VOL_UUID)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'connect_to_portals')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_device_path')
|
||||
|
@ -458,7 +446,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector._connect_target_volume, 'fakenqn',
|
||||
'fakeuuid', [fake_portal])
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, 'fakenqn', 'fakeuuid')
|
||||
'fakenqn', 'fakeuuid')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'connect_to_portals')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_device_path')
|
||||
|
@ -470,22 +458,22 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
'fakenqn', 'fakeuuid', [('fake', 'portal', 'tcp')]),
|
||||
'/dev/nvme0n1')
|
||||
mock_connect_portal.assert_called_with(
|
||||
self.connector, 'fakenqn', [('fake', 'portal', 'tcp')])
|
||||
'fakenqn', [('fake', 'portal', 'tcp')])
|
||||
mock_device_path.assert_called_with(
|
||||
self.connector, 'fakenqn', 'fakeuuid')
|
||||
'fakenqn', 'fakeuuid')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_nvme_cli')
|
||||
def test_connect_to_portals(self, mock_nvme_cli):
|
||||
nvme_command = (
|
||||
'connect', '-a', '10.0.0.1', '-s', 4420, '-t',
|
||||
'tcp', '-n', 'fakenqn', '-Q', '128', '-l', '-1')
|
||||
self.assertEqual(
|
||||
self.connector.connect_to_portals(
|
||||
self.connector, 'fakenqn', [('10.0.0.1', 4420, 'tcp')]),
|
||||
'fakenqn', [('10.0.0.1', 4420, 'tcp')]),
|
||||
True)
|
||||
mock_nvme_cli.assert_called_with(self.connector, nvme_command)
|
||||
mock_nvme_cli.assert_called_with(nvme_command)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_nvme_cli')
|
||||
def test_connect_to_portals_rdma_no_conn(self, mock_nvme_cli):
|
||||
mock_nvme_cli.side_effect = Exception()
|
||||
nvme_command = (
|
||||
|
@ -493,9 +481,9 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
'rdma', '-n', 'fakenqn', '-Q', '128', '-l', '-1')
|
||||
self.assertEqual(
|
||||
self.connector.connect_to_portals(
|
||||
self.connector, 'fakenqn', [('10.0.0.1', 4420, 'RoCEv2')]),
|
||||
'fakenqn', [('10.0.0.1', 4420, 'RoCEv2')]),
|
||||
False)
|
||||
mock_nvme_cli.assert_called_with(self.connector, nvme_command)
|
||||
mock_nvme_cli.assert_called_with(nvme_command)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'stop_and_assemble_raid')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_is_device_in_raid')
|
||||
|
@ -507,9 +495,9 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
'fakealias', 3),
|
||||
'/dev/md/fakealias')
|
||||
mock_device_raid.assert_called_with(self.connector, '/dev/nvme1n1')
|
||||
mock_device_raid.assert_called_with('/dev/nvme1n1')
|
||||
mock_stop_assemble_raid.assert_called_with(
|
||||
self.connector, ['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
'/dev/md/fakealias', False)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_is_device_in_raid')
|
||||
|
@ -520,9 +508,9 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.connector._handle_replicated_volume,
|
||||
['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
'fakealias', 4)
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n1')
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n2')
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n3')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n1')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n2')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n3')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'create_raid')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_is_device_in_raid')
|
||||
|
@ -534,22 +522,23 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
'fakealias', 3),
|
||||
'/dev/md/fakealias')
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n1')
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n2')
|
||||
mock_device_raid.assert_any_call(self.connector, '/dev/nvme1n3')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n1')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n2')
|
||||
mock_device_raid.assert_any_call('/dev/nvme1n3')
|
||||
mock_create_raid.assert_called_with(
|
||||
self.connector, ['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
['/dev/nvme1n1', '/dev/nvme1n2', '/dev/nvme1n3'],
|
||||
'1', 'fakealias', 'fakealias', False)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'stop_raid')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'ks_readlink')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_md_name')
|
||||
def test_stop_and_assemble_raid_existing_simple(
|
||||
self, mock_md_name, mock_readlink):
|
||||
self, mock_md_name, mock_readlink, mock_stopraid):
|
||||
mock_readlink.return_value = ''
|
||||
mock_md_name.return_value = 'mdalias'
|
||||
self.assertIsNone(self.connector.stop_and_assemble_raid(
|
||||
self.connector, ['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with(self.connector, 'sda')
|
||||
['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with('sda')
|
||||
mock_readlink.assert_called_with('/dev/md/mdalias')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'ks_readlink')
|
||||
|
@ -559,51 +548,49 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_readlink.return_value = '/dev/md/mdalias'
|
||||
mock_md_name.return_value = 'mdalias'
|
||||
self.assertIsNone(self.connector.stop_and_assemble_raid(
|
||||
self.connector, ['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with(self.connector, 'sda')
|
||||
['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with('sda')
|
||||
mock_readlink.assert_called_with('/dev/md/mdalias')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'stop_raid')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'assemble_raid')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'ks_readlink')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_md_name')
|
||||
def test_stop_and_assemble_raid_err(self, mock_md_name, mock_readlink,
|
||||
mock_assemble):
|
||||
mock_assemble, mock_stopraid):
|
||||
mock_readlink.return_value = '/dev/md/mdalias'
|
||||
mock_md_name.return_value = 'dummy'
|
||||
mock_assemble.side_effect = Exception()
|
||||
self.assertIsNone(self.connector.stop_and_assemble_raid(
|
||||
self.connector, ['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with(self.connector, 'sda')
|
||||
['/dev/sda'], '/dev/md/mdalias', False))
|
||||
mock_md_name.assert_called_with('sda')
|
||||
mock_readlink.assert_called_with('/dev/md/mdalias')
|
||||
mock_assemble.assert_called_with(self.connector, ['/dev/sda'],
|
||||
mock_assemble.assert_called_with(['/dev/sda'],
|
||||
'/dev/md/mdalias', False)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_assemble_raid_simple(self, mock_run_mdadm):
|
||||
self.assertEqual(self.connector.assemble_raid(
|
||||
self.connector, ['/dev/sda'], '/dev/md/md1', True), True)
|
||||
['/dev/sda'], '/dev/md/md1', True), True)
|
||||
mock_run_mdadm.assert_called_with(
|
||||
self.connector,
|
||||
['mdadm', '--assemble', '--run', '/dev/md/md1', '-o', '/dev/sda'],
|
||||
True)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_assemble_raid_simple_err(self, mock_run_mdadm):
|
||||
mock_run_mdadm.side_effect = putils.ProcessExecutionError()
|
||||
self.assertRaises(putils.ProcessExecutionError,
|
||||
self.connector.assemble_raid, self.connector,
|
||||
self.connector.assemble_raid,
|
||||
['/dev/sda'], '/dev/md/md1', True)
|
||||
mock_run_mdadm.assert_called_with(
|
||||
self.connector,
|
||||
['mdadm', '--assemble', '--run', '/dev/md/md1', '-o', '/dev/sda'],
|
||||
True)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_create_raid_cmd_simple(self, mock_run_mdadm):
|
||||
self.assertIsNone(self.connector.create_raid(
|
||||
self.connector, ['/dev/sda'], '1', 'md1', 'name', True))
|
||||
['/dev/sda'], '1', 'md1', 'name', True))
|
||||
mock_run_mdadm.assert_called_with(
|
||||
self.connector,
|
||||
['mdadm', '-C', '-o', 'md1', '-R', '-N', 'name', '--level', '1',
|
||||
'--raid-devices=1', '--bitmap=internal', '--homehost=any',
|
||||
'--failfast', '--assume-clean', '/dev/sda'])
|
||||
|
@ -614,9 +601,9 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_raid_exists.return_value = True
|
||||
mock_stop_raid.return_value = False
|
||||
self.assertIsNone(self.connector.end_raid(
|
||||
self.connector, '/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with(self.connector, '/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with(self.connector, '/dev/md/md1')
|
||||
'/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with('/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with('/dev/md/md1')
|
||||
|
||||
@mock.patch.object(os.path, 'exists')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'stop_raid')
|
||||
|
@ -626,9 +613,9 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_stop_raid.return_value = False
|
||||
mock_os.return_value = True
|
||||
self.assertIsNone(self.connector.end_raid(
|
||||
self.connector, '/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with(self.connector, '/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with(self.connector, '/dev/md/md1')
|
||||
'/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with('/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with('/dev/md/md1')
|
||||
mock_os.assert_called_with('/dev/md/md1')
|
||||
|
||||
@mock.patch.object(os.path, 'exists')
|
||||
|
@ -639,86 +626,77 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_stop_raid.side_effect = Exception()
|
||||
mock_os.return_value = True
|
||||
self.assertIsNone(self.connector.end_raid(
|
||||
self.connector, '/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with(self.connector, '/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with(self.connector, '/dev/md/md1')
|
||||
'/dev/md/md1'))
|
||||
mock_raid_exists.assert_called_with('/dev/md/md1')
|
||||
mock_stop_raid.assert_called_with('/dev/md/md1')
|
||||
mock_os.assert_called_with('/dev/md/md1')
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_stop_raid_simple(self, mock_run_mdadm):
|
||||
mock_run_mdadm.return_value = 'mdadm output'
|
||||
mock_run_mdadm.return_value = 'mdadm output', None
|
||||
self.assertEqual(self.connector.stop_raid(
|
||||
self.connector, '/dev/md/md1'), 'mdadm output')
|
||||
'/dev/md/md1'), 'mdadm output')
|
||||
mock_run_mdadm.assert_called_with(
|
||||
self.connector, ['mdadm', '--stop', '/dev/md/md1'])
|
||||
['mdadm', '--stop', '/dev/md/md1'])
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_mdadm')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_remove_raid_simple(self, mock_run_mdadm):
|
||||
self.assertIsNone(self.connector.remove_raid(
|
||||
self.connector, '/dev/md/md1'))
|
||||
'/dev/md/md1'))
|
||||
mock_run_mdadm.assert_called_with(
|
||||
self.connector, ['mdadm', '--remove', '/dev/md/md1'])
|
||||
['mdadm', '--remove', '/dev/md/md1'])
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_nvme_controller')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_controller')
|
||||
def test_rescan(self, mock_get_nvme_controller, mock_run_nvme_cli):
|
||||
mock_get_nvme_controller.return_value = 'nvme1'
|
||||
mock_run_nvme_cli.return_value = None
|
||||
result = self.connector.rescan(EXECUTOR, TARGET_NQN, VOL_UUID)
|
||||
result = self.connector.rescan(TARGET_NQN, VOL_UUID)
|
||||
self.assertIsNone(result)
|
||||
mock_get_nvme_controller.assert_called_with(EXECUTOR, TARGET_NQN)
|
||||
mock_get_nvme_controller.assert_called_with(TARGET_NQN)
|
||||
nvme_command = ('ns-rescan', NVME_DEVICE_PATH)
|
||||
mock_run_nvme_cli.assert_called_with(EXECUTOR, nvme_command)
|
||||
mock_run_nvme_cli.assert_called_with(nvme_command)
|
||||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_nvme_controller')
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_nvme_cli')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_controller')
|
||||
def test_rescan_err(self, mock_get_nvme_controller, mock_run_nvme_cli):
|
||||
mock_get_nvme_controller.return_value = 'nvme1'
|
||||
mock_run_nvme_cli.side_effect = Exception()
|
||||
self.assertRaises(exception.CommandExecutionFailed,
|
||||
self.connector.rescan, EXECUTOR, TARGET_NQN,
|
||||
self.connector.rescan, TARGET_NQN,
|
||||
VOL_UUID)
|
||||
mock_get_nvme_controller.assert_called_with(EXECUTOR, TARGET_NQN)
|
||||
mock_get_nvme_controller.assert_called_with(TARGET_NQN)
|
||||
nvme_command = ('ns-rescan', NVME_DEVICE_PATH)
|
||||
mock_run_nvme_cli.assert_called_with(EXECUTOR, nvme_command)
|
||||
mock_run_nvme_cli.assert_called_with(nvme_command)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_raid_exists_not(self, mock_execute):
|
||||
mock_execute.return_value = (VOL_UUID + "\n", "")
|
||||
result = self.connector.is_raid_exists(EXECUTOR, NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_raid_exists_not(self, mock_runmdadm):
|
||||
mock_runmdadm.return_value = (VOL_UUID + "\n", "")
|
||||
result = self.connector.is_raid_exists(NVME_DEVICE_PATH)
|
||||
self.assertEqual(False, result)
|
||||
cmd = ['mdadm', '--detail', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_raid_exists(self, mock_execute):
|
||||
mock_execute.return_value = (NVME_DEVICE_PATH + ':' + "\n", "")
|
||||
result = self.connector.is_raid_exists(EXECUTOR, NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_raid_exists(self, mock_runmdadm):
|
||||
mock_runmdadm.return_value = (NVME_DEVICE_PATH + ':' + "\n", "")
|
||||
result = self.connector.is_raid_exists(NVME_DEVICE_PATH)
|
||||
self.assertEqual(True, result)
|
||||
cmd = ['mdadm', '--detail', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_raid_exists_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError
|
||||
result = self.connector.is_raid_exists(EXECUTOR, NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_raid_exists_err(self, mock_runmdadm):
|
||||
mock_runmdadm.side_effect = putils.ProcessExecutionError
|
||||
result = self.connector.is_raid_exists(NVME_DEVICE_PATH)
|
||||
self.assertEqual(False, result)
|
||||
cmd = ['mdadm', '--detail', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute')
|
||||
def test_get_md_name(self, mock_execute):
|
||||
mock_execute.return_value = ('nvme1' + "\n", "")
|
||||
result = self.connector.get_md_name(EXECUTOR, NVME_DEVICE_PATH)
|
||||
result = self.connector.get_md_name(NVME_DEVICE_PATH)
|
||||
self.assertEqual('nvme1', result)
|
||||
get_md_cmd = 'cat /proc/mdstat | grep /dev/nvme1 | awk \'{print $1;}\''
|
||||
cmd = ['bash', '-c', get_md_cmd]
|
||||
|
@ -727,10 +705,10 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute')
|
||||
def test_get_md_name_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
result = self.connector.get_md_name(EXECUTOR, NVME_DEVICE_PATH)
|
||||
result = self.connector.get_md_name(NVME_DEVICE_PATH)
|
||||
self.assertIsNone(result)
|
||||
get_md_cmd = 'cat /proc/mdstat | grep /dev/nvme1 | awk \'{print $1;}\''
|
||||
cmd = ['bash', '-c', get_md_cmd]
|
||||
|
@ -739,116 +717,82 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_device_in_raid(self, mock_execute):
|
||||
mock_execute.return_value = (NVME_DEVICE_PATH + ':' + "\n", "")
|
||||
result = self.connector._is_device_in_raid(self.connector,
|
||||
NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_device_in_raid(self, mock_runmdadm):
|
||||
mock_runmdadm.return_value = (NVME_DEVICE_PATH + ':' + "\n", "")
|
||||
result = self.connector._is_device_in_raid(NVME_DEVICE_PATH)
|
||||
self.assertEqual(True, result)
|
||||
cmd = ['mdadm', '--examine', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_device_in_raid_not_found(self, mock_execute):
|
||||
mock_execute.return_value = (VOL_UUID + "\n", "")
|
||||
result = self.connector._is_device_in_raid(self.connector,
|
||||
NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_device_in_raid_not_found(self, mock_runmdadm):
|
||||
mock_runmdadm.return_value = (VOL_UUID + "\n", "")
|
||||
result = self.connector._is_device_in_raid(NVME_DEVICE_PATH)
|
||||
self.assertEqual(False, result)
|
||||
cmd = ['mdadm', '--examine', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_is_device_in_raid_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
result = self.connector._is_device_in_raid(self.connector,
|
||||
NVME_DEVICE_PATH)
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'run_mdadm')
|
||||
def test_is_device_in_raid_err(self, mock_runmdadm):
|
||||
mock_runmdadm.side_effect = putils.ProcessExecutionError()
|
||||
result = self.connector._is_device_in_raid(NVME_DEVICE_PATH)
|
||||
self.assertEqual(False, result)
|
||||
cmd = ['mdadm', '--examine', NVME_DEVICE_PATH]
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
mock_runmdadm.assert_called_with(cmd)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_run_mdadm(self, mock_execute):
|
||||
mock_execute.return_value = (VOL_UUID + "\n", "")
|
||||
cmd = ['mdadm', '--examine', NVME_DEVICE_PATH]
|
||||
result = self.connector.run_mdadm(EXECUTOR, cmd)
|
||||
self.assertEqual(VOL_UUID, result)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_run_mdadm_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
cmd = ['mdadm', '--examine', NVME_DEVICE_PATH]
|
||||
result = self.connector.run_mdadm(EXECUTOR, cmd)
|
||||
self.assertIsNone(result)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
@mock.patch.object(os.path, 'exists')
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute')
|
||||
@mock.patch.object(glob, 'glob')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_nvme_controller')
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, 'get_nvme_controller')
|
||||
def test_get_nvme_device_path(self, mock_get_nvme_controller, mock_glob,
|
||||
mock_execute):
|
||||
mock_execute, mock_os_exists):
|
||||
mock_get_nvme_controller.return_value = 'nvme1'
|
||||
block_dev_path = '/sys/class/nvme-fabrics/ctl/nvme1/nvme1n*'
|
||||
mock_glob.side_effect = [['/sys/class/nvme-fabrics/ctl/nvme1/nvme1n1']]
|
||||
mock_os_exists.return_value = True
|
||||
block_dev_path = '/sys/class/block/nvme1n*'
|
||||
mock_glob.return_value = ['/sys/class/block/nvme1n1']
|
||||
mock_execute.return_value = (VOL_UUID + "\n", "")
|
||||
cmd = ['cat', '/sys/class/nvme-fabrics/ctl/nvme1/nvme1n1/uuid']
|
||||
result = self.connector.get_nvme_device_path(EXECUTOR, TARGET_NQN,
|
||||
cmd = ['cat', '/sys/class/block/nvme1n1/uuid']
|
||||
result = self.connector.get_nvme_device_path(TARGET_NQN,
|
||||
VOL_UUID)
|
||||
mock_get_nvme_controller.assert_called_with(EXECUTOR, TARGET_NQN)
|
||||
mock_get_nvme_controller.assert_called_with(TARGET_NQN)
|
||||
self.assertEqual(NVME_NS_PATH, result)
|
||||
mock_glob.assert_any_call(block_dev_path)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
mock_execute.assert_called_with(*cmd)
|
||||
|
||||
def execute_side_effect(self, value, run_as_root, root_helper):
|
||||
def execute_side_effect(self, value):
|
||||
if 'nqn' in value:
|
||||
return TARGET_NQN + "\n", ""
|
||||
if 'state' in value:
|
||||
return 'live' + "\n", ""
|
||||
|
||||
def execute_side_effect_not_live(self, value, run_as_root, root_helper):
|
||||
def execute_side_effect_not_live(self, value):
|
||||
if 'nqn' in value:
|
||||
return TARGET_NQN + "\n", ""
|
||||
if 'state' in value:
|
||||
return 'dead' + "\n", ""
|
||||
|
||||
def execute_side_effect_not_found(self, value, run_as_root, root_helper):
|
||||
def execute_side_effect_not_found(self, value):
|
||||
if 'nqn' in value:
|
||||
return "dummy" + "\n", ""
|
||||
if 'state' in value:
|
||||
return 'live' + "\n", ""
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute',
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute',
|
||||
side_effect=execute_side_effect)
|
||||
@mock.patch.object(glob, 'glob')
|
||||
def test_get_nvme_controller(self, mock_glob, mock_execute):
|
||||
ctrl_path = '/sys/class/nvme-fabrics/ctl/nvme*'
|
||||
mock_glob.side_effect = [['/sys/class/nvme-fabrics/ctl/nvme1']]
|
||||
cmd = ['cat', '/sys/class/nvme-fabrics/ctl/nvme1/state']
|
||||
result = self.connector._get_nvme_controller(EXECUTOR, TARGET_NQN)
|
||||
result = self.connector.get_nvme_controller(TARGET_NQN)
|
||||
self.assertEqual('nvme1', result)
|
||||
mock_glob.assert_any_call(ctrl_path)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute',
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute',
|
||||
side_effect=execute_side_effect_not_live)
|
||||
@mock.patch.object(glob, 'glob')
|
||||
def test_get_nvme_controller_not_live(self, mock_glob, mock_execute):
|
||||
|
@ -856,14 +800,14 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_glob.side_effect = [['/sys/class/nvme-fabrics/ctl/nvme1']]
|
||||
cmd = ['cat', '/sys/class/nvme-fabrics/ctl/nvme1/state']
|
||||
self.assertRaises(exception.VolumeDeviceNotFound,
|
||||
self.connector._get_nvme_controller, EXECUTOR,
|
||||
self.connector.get_nvme_controller,
|
||||
TARGET_NQN)
|
||||
mock_glob.assert_any_call(ctrl_path)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute',
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute',
|
||||
side_effect=execute_side_effect_not_found)
|
||||
@mock.patch.object(glob, 'glob')
|
||||
def test_get_nvme_controller_not_found(self, mock_glob, mock_execute):
|
||||
|
@ -871,7 +815,7 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_glob.side_effect = [['/sys/class/nvme-fabrics/ctl/nvme1']]
|
||||
cmd = ['cat', '/sys/class/nvme-fabrics/ctl/nvme1/state']
|
||||
self.assertRaises(exception.VolumeDeviceNotFound,
|
||||
self.connector._get_nvme_controller, EXECUTOR,
|
||||
self.connector.get_nvme_controller,
|
||||
TARGET_NQN)
|
||||
mock_glob.assert_any_call(ctrl_path)
|
||||
args, kwargs = mock_execute.call_args
|
||||
|
@ -904,19 +848,12 @@ class NVMeOFConnectorTestCase(test_connector.ConnectorTestCase):
|
|||
mock_create.assert_not_called()
|
||||
self.assertIsNone(result)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
def test_run_nvme_cli(self, mock_execute):
|
||||
mock_execute.return_value = ("\n", "")
|
||||
cmd = 'dummy command'
|
||||
result = self.connector.run_nvme_cli(EXECUTOR, cmd)
|
||||
self.assertEqual(("\n", ""), result)
|
||||
|
||||
def test_ks_readlink(self):
|
||||
dest = 'dummy path'
|
||||
result = self.connector.ks_readlink(dest)
|
||||
self.assertEqual('', result)
|
||||
|
||||
@mock.patch.object(executor.Executor, '_execute')
|
||||
@mock.patch.object(nvmeof.priv_rootwrap, 'custom_execute')
|
||||
def test_get_fs_type_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
result = self.connector._get_fs_type(NVME_DEVICE_PATH)
|
||||
|
|
|
@ -43,7 +43,7 @@ class ConnectorUtilsTestCase(test_base.TestCase):
|
|||
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_system_uuid',
|
||||
return_value=None)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_uuid',
|
||||
@mock.patch.object(nvmeof.priv_nvme, 'get_host_uuid',
|
||||
return_value=None)
|
||||
@mock.patch.object(nvmeof.NVMeOFConnector, '_get_host_nqn',
|
||||
return_value=None)
|
||||
|
|
|
@ -95,3 +95,42 @@ class PrivNVMeTestCase(base.TestCase):
|
|||
exist_ok=True)
|
||||
mock_exec.assert_called_once_with('nvme', 'show-hostnqn')
|
||||
self.assertEqual('', res)
|
||||
|
||||
@mock.patch.object(rootwrap, 'custom_execute')
|
||||
def test_get_host_uuid(self, mock_execute):
|
||||
mock_execute.return_value = 'fakeuuid', ''
|
||||
self.assertEqual('fakeuuid', privsep_nvme.get_host_uuid())
|
||||
|
||||
@mock.patch.object(rootwrap, 'custom_execute')
|
||||
def test_get_host_uuid_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
self.assertIsNone(privsep_nvme.get_host_uuid())
|
||||
|
||||
@mock.patch.object(rootwrap, 'custom_execute')
|
||||
def test_run_nvme_cli(self, mock_execute):
|
||||
mock_execute.return_value = ("\n", "")
|
||||
cmd = 'dummy command'
|
||||
result = privsep_nvme.run_nvme_cli(cmd)
|
||||
self.assertEqual(("\n", ""), result)
|
||||
|
||||
@mock.patch.object(rootwrap, 'custom_execute')
|
||||
def test_run_mdadm(self, mock_execute):
|
||||
mock_execute.return_value = "fakeuuid"
|
||||
cmd = ['mdadm', '--examine', '/dev/nvme1']
|
||||
result = privsep_nvme.run_mdadm(cmd)
|
||||
self.assertEqual('fakeuuid', result)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
||||
@mock.patch.object(rootwrap, 'custom_execute')
|
||||
def test_run_mdadm_err(self, mock_execute):
|
||||
mock_execute.side_effect = putils.ProcessExecutionError()
|
||||
cmd = ['mdadm', '--examine', '/dev/nvme1']
|
||||
result = privsep_nvme.run_mdadm(cmd)
|
||||
self.assertIsNone(result)
|
||||
args, kwargs = mock_execute.call_args
|
||||
self.assertEqual(args[0], cmd[0])
|
||||
self.assertEqual(args[1], cmd[1])
|
||||
self.assertEqual(args[2], cmd[2])
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
`https://blueprints.launchpad.net/cinder/+spec/nvmeof-connection-agent`
|
||||
NVMeOF Connection Agent
|
||||
A console script that when launched, is responsible for initiator-side
|
||||
monitoring and self healing of NVMeoF connections and replicated mdraid
|
||||
volumes.
|
Loading…
Reference in New Issue