os-brick/os_brick/initiator/connectors/aoe.py

177 lines
6.3 KiB
Python

# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_concurrency import lockutils
from oslo_log import log as logging
from oslo_service import loopingcall
from os_brick import exception
from os_brick import initiator
from os_brick.initiator.connectors import base
from os_brick import utils
DEVICE_SCAN_ATTEMPTS_DEFAULT = 3
LOG = logging.getLogger(__name__)
class AoEConnector(base.BaseLinuxConnector):
"""Connector class to attach/detach AoE volumes."""
def __init__(self, root_helper, driver=None,
device_scan_attempts=initiator.DEVICE_SCAN_ATTEMPTS_DEFAULT,
*args, **kwargs):
super(AoEConnector, self).__init__(
root_helper,
driver=driver,
device_scan_attempts=device_scan_attempts,
*args, **kwargs)
@staticmethod
def get_connector_properties(root_helper, *args, **kwargs):
"""The AoE connector properties."""
return {}
def get_search_path(self):
return '/dev/etherd'
def get_volume_paths(self, connection_properties):
aoe_device, aoe_path = self._get_aoe_info(connection_properties)
volume_paths = []
if os.path.exists(aoe_path):
volume_paths.append(aoe_path)
return volume_paths
def _get_aoe_info(self, connection_properties):
shelf = connection_properties['target_shelf']
lun = connection_properties['target_lun']
aoe_device = 'e%(shelf)s.%(lun)s' % {'shelf': shelf,
'lun': lun}
path = self.get_search_path()
aoe_path = '%(path)s/%(device)s' % {'path': path,
'device': aoe_device}
return aoe_device, aoe_path
@utils.trace
@lockutils.synchronized('aoe_control', 'aoe-')
def connect_volume(self, connection_properties):
"""Discover and attach the volume.
:param connection_properties: The dictionary that describes all
of the target volume attributes.
:type connection_properties: dict
:returns: dict
connection_properties for AoE must include:
target_shelf - shelf id of volume
target_lun - lun id of volume
"""
aoe_device, aoe_path = self._get_aoe_info(connection_properties)
device_info = {
'type': 'block',
'device': aoe_device,
'path': aoe_path,
}
if os.path.exists(aoe_path):
self._aoe_revalidate(aoe_device)
else:
self._aoe_discover()
waiting_status = {'tries': 0}
# NOTE(jbr_): Device path is not always present immediately
def _wait_for_discovery(aoe_path):
if os.path.exists(aoe_path):
raise loopingcall.LoopingCallDone
if waiting_status['tries'] >= self.device_scan_attempts:
raise exception.VolumeDeviceNotFound(device=aoe_path)
LOG.info("AoE volume not yet found at: %(path)s. "
"Try number: %(tries)s",
{'path': aoe_device, 'tries': waiting_status['tries']})
self._aoe_discover()
waiting_status['tries'] += 1
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_discovery,
aoe_path)
timer.start(interval=2).wait()
if waiting_status['tries']:
LOG.debug("Found AoE device %(path)s "
"(after %(tries)s rediscover)",
{'path': aoe_path,
'tries': waiting_status['tries']})
return device_info
@utils.trace
@lockutils.synchronized('aoe_control', 'aoe-')
def disconnect_volume(self, connection_properties, device_info,
force=False, ignore_errors=False):
"""Detach and flush the volume.
:param connection_properties: The dictionary that describes all
of the target volume attributes.
:type connection_properties: dict
:param device_info: historical difference, but same as connection_props
:type device_info: dict
connection_properties for AoE must include:
target_shelf - shelf id of volume
target_lun - lun id of volume
"""
aoe_device, aoe_path = self._get_aoe_info(connection_properties)
if os.path.exists(aoe_path):
self._aoe_flush(aoe_device)
def _aoe_discover(self):
(out, err) = self._execute('aoe-discover',
run_as_root=True,
root_helper=self._root_helper,
check_exit_code=0)
LOG.debug('aoe-discover: stdout=%(out)s stderr%(err)s',
{'out': out, 'err': err})
def _aoe_revalidate(self, aoe_device):
(out, err) = self._execute('aoe-revalidate',
aoe_device,
run_as_root=True,
root_helper=self._root_helper,
check_exit_code=0)
LOG.debug('aoe-revalidate %(dev)s: stdout=%(out)s stderr%(err)s',
{'dev': aoe_device, 'out': out, 'err': err})
def _aoe_flush(self, aoe_device):
(out, err) = self._execute('aoe-flush',
aoe_device,
run_as_root=True,
root_helper=self._root_helper,
check_exit_code=0)
LOG.debug('aoe-flush %(dev)s: stdout=%(out)s stderr%(err)s',
{'dev': aoe_device, 'out': out, 'err': err})
def extend_volume(self, connection_properties):
# TODO(walter-boring): is this possible?
raise NotImplementedError