3998N/A# vim: tabstop=4 shiftwidth=4 softtabstop=4
3998N/A
3998N/A# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3998N/A#
3998N/A# Licensed under the Apache License, Version 2.0 (the "License"); you may
3998N/A# not use this file except in compliance with the License. You may obtain
3998N/A# a copy of the License at
3998N/A#
3998N/A# http://www.apache.org/licenses/LICENSE-2.0
3998N/A#
3998N/A# Unless required by applicable law or agreed to in writing, software
3998N/A# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3998N/A# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3998N/A# License for the specific language governing permissions and limitations
3998N/A# under the License.
3998N/A
3998N/A"""Generic Solaris iSCSI utilities."""
3998N/A
3998N/Aimport os
4791N/Aimport platform
3998N/Aimport time
3998N/A
5403N/Afrom oslo_concurrency import processutils as putils
5403N/Afrom oslo_log import log as logging
5403N/A
6759N/Afrom os_brick import exception
6759N/Aimport oslo_i18n
3998N/A
3998N/ALOG = logging.getLogger(__name__)
3998N/A
3998N/A
3998N/Aclass SolarisiSCSI(object):
3998N/A def __init__(self, *args, **kwargs):
3998N/A self.execute = putils.execute
3998N/A
3998N/A def _get_device_path(self, connection_properties):
5085N/A """Get the device path from the target info.
5085N/A
5085N/A The output of cmd below is like this:
5085N/A Target: iqn.2010-10.org.openstack:hostname1-tgt-grp-target
5085N/A Alias: -
5085N/A TPGT: 1
5085N/A ISID: 4000002a0000
5085N/A Connections: 1
5085N/A LUN: 1
5085N/A Vendor: SUN
5085N/A Product: COMSTAR
5085N/A OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C04030003d0s2
5085N/A LUN: 0
5085N/A Vendor: SUN
5085N/A Product: COMSTAR
5085N/A OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C02270002d0s2
5085N/A
5085N/A """
3998N/A (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list',
3998N/A 'target', '-S',
3998N/A connection_properties['target_iqn'])
3998N/A
5085N/A found = False
3998N/A for line in [l.strip() for l in out.splitlines()]:
5085N/A if line.startswith("LUN:"):
5085N/A lun = line.split()[-1]
5085N/A if int(lun) == int(connection_properties['target_lun']):
5085N/A found = True
5085N/A continue
5085N/A if found:
5085N/A if line.startswith("OS Device Name:"):
5085N/A dev_path = line.split()[-1]
5085N/A return dev_path
5085N/A elif line.startswith("LUN:"):
5085N/A found = False
5085N/A
5085N/A if not found:
5085N/A LOG.error(_("No device is found for the target %s LUN %s.") %
5085N/A (connection_properties['target_iqn'],
5085N/A connection_properties['target_lun']))
3998N/A raise
3998N/A
3998N/A def get_initiator(self):
3998N/A """Return the iSCSI initiator node name IQN"""
3998N/A out, err = self.execute('/usr/sbin/iscsiadm', 'list', 'initiator-node')
3998N/A
3998N/A # Sample first line of command output:
3998N/A # Initiator node name: iqn.1986-03.com.sun:01:e00000000000.4f757217
3998N/A initiator_name_line = out.splitlines()[0]
3998N/A return initiator_name_line.rsplit(' ', 1)[1]
3998N/A
3998N/A def _connect_to_iscsi_portal(self, connection_properties):
3998N/A # TODO(Strony): handle the CHAP authentication
3998N/A target_ip = connection_properties['target_portal'].split(":")[0]
3998N/A self.execute('/usr/sbin/iscsiadm', 'add', 'discovery-address',
3998N/A target_ip)
3998N/A self.execute('/usr/sbin/iscsiadm', 'modify', 'discovery',
3998N/A '--sendtargets', 'enable')
3998N/A (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list',
3998N/A 'discovery-address', '-v',
3998N/A target_ip)
3998N/A
3998N/A lines = out.splitlines()
3998N/A if not lines[0].strip().startswith('Discovery Address: ') or \
3998N/A lines[1].strip().startswith('Unable to get targets.'):
3998N/A msg = _("No iSCSI target is found.")
3998N/A LOG.error(msg)
3998N/A raise
3998N/A
3998N/A target_iqn = connection_properties['target_iqn']
3998N/A for line in [l.strip() for l in lines]:
3998N/A if line.startswith("Target name:") and \
3998N/A line.split()[-1] == target_iqn:
3998N/A return
3998N/A else:
3998N/A LOG.error(_("No active session is found for the target %s.") %
3998N/A target_iqn)
3998N/A raise
3998N/A
3998N/A def connect_volume(self, connection_properties, scan_tries):
3998N/A """Attach the volume to instance_name.
3998N/A
3998N/A connection_properties for iSCSI must include:
3998N/A target_portal - ip and optional port
3998N/A target_iqn - iSCSI Qualified Name
3998N/A target_lun - LUN id of the volume
3998N/A """
3998N/A device_info = {'type': 'block'}
3998N/A
3998N/A # TODO(Strony): support the iSCSI multipath on Solaris.
3998N/A self._connect_to_iscsi_portal(connection_properties)
3998N/A
3998N/A host_device = self._get_device_path(connection_properties)
3998N/A
3998N/A # check if it is a valid device path.
3998N/A for i in range(1, scan_tries):
3998N/A if os.path.exists(host_device):
3998N/A break
3998N/A else:
3998N/A time.sleep(i ** 2)
3998N/A else:
3998N/A raise exception.VolumeDeviceNotFound(device=host_device)
3998N/A
4791N/A # Set the label EFI to the disk on SPARC before it is accessed and
4791N/A # make sure the correct device path with slice 0
4791N/A # (like '/dev/rdsk/c0t600xxxd0s0').
4791N/A if platform.processor() == 'sparc':
4791N/A tmp_dev_name = host_device.rsplit('s', 1)
4791N/A disk_name = tmp_dev_name[0].split('/')[-1]
4791N/A (out, _err) = self.execute('/usr/sbin/format', '-L', 'efi', '-d',
4791N/A disk_name)
4791N/A host_device = '%ss0' % tmp_dev_name[0]
4791N/A
3998N/A device_info['path'] = host_device
3998N/A return device_info