text: # Copyright (C) 2014, Hitachi, Ltd.
  #
  #    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.
  """
  iSCSI Cinder volume driver for Hitachi storage.
  
  """
  
  import os
  import threading
  
  from oslo_config import cfg
  from oslo_log import log as logging
  import six
  
  from cinder import exception
  from cinder.i18n import _LE, _LI
  from cinder import utils
  import cinder.volume.driver
  from cinder.volume.drivers.hitachi import hbsd_basiclib as basic_lib
  from cinder.volume.drivers.hitachi import hbsd_common as common
  
  LOG = logging.getLogger(__name__)
  
  CHAP_METHOD = ('None', 'CHAP None', 'CHAP')
  
  volume_opts = [
      cfg.BoolOpt('hitachi_add_chap_user',
                  default=False,
                  help='Add CHAP user'),
      cfg.StrOpt('hitachi_auth_method',
                 default=None,
                 help='iSCSI authentication method'),
      cfg.StrOpt('hitachi_auth_user',
                 default='%sCHAP-user' % basic_lib.NAME_PREFIX,
                 help='iSCSI authentication username'),
      cfg.StrOpt('hitachi_auth_password',
                 default='%sCHAP-password' % basic_lib.NAME_PREFIX,
                 help='iSCSI authentication password', secret=True),
  ]
  
  CONF = cfg.CONF
  CONF.register_opts(volume_opts)
  
  
  class HBSDISCSIDriver(cinder.volume.driver.ISCSIDriver):
      VERSION = common.VERSION
  
      def __init__(self, *args, **kwargs):
          os.environ['LANG'] = 'C'
          super(HBSDISCSIDriver, self).__init__(*args, **kwargs)
          self.db = kwargs.get('db')
          self.common = None
          self.configuration.append_config_values(common.volume_opts)
          self._stats = {}
          self.context = None
          self.do_setup_status = threading.Event()
  
      def _check_param(self):
          self.configuration.append_config_values(volume_opts)
          if (self.configuration.hitachi_auth_method and
                  self.configuration.hitachi_auth_method not in CHAP_METHOD):
              raise exception.HBSDError(
                  message=basic_lib.output_err(601, param='hitachi_auth_method'))
          if self.configuration.hitachi_auth_method == 'None':
              self.configuration.hitachi_auth_method = None
          for opt in volume_opts:
              getattr(self.configuration, opt.name)
  
      def check_param(self):
          try:
              self.common.check_param()
              self._check_param()
          except exception.HBSDError:
              raise
          except Exception as ex:
              raise exception.HBSDError(
                  message=basic_lib.output_err(601, param=six.text_type(ex)))
  
      def output_param_to_log(self):
          lock = basic_lib.get_process_lock(self.common.system_lock_file)
  
          with lock:
              self.common.output_param_to_log('iSCSI')
              for opt in volume_opts:
                  if not opt.secret:
                      value = getattr(self.configuration, opt.name)
                      LOG.info(_LI('\t%(name)-35s : %(value)s'),
                               {'name': opt.name, 'value': value})
  
      def _delete_lun_iscsi(self, hostgroups, ldev):
          try:
              self.common.command.comm_delete_lun_iscsi(hostgroups, ldev)
          except exception.HBSDNotFound:
              LOG.warning(basic_lib.set_msg(301, ldev=ldev))
  
      def _add_target(self, hostgroups, ldev):
          self.common.add_lun('autargetmap', hostgroups, ldev)
  
      def _add_initiator(self, hgs, port, gid, host_iqn):
          self.common.command.comm_add_initiator(port, gid, host_iqn)
          hgs.append({'port': port, 'gid': int(gid), 'detected': True})
          LOG.debug("Create iSCSI target for %s", hgs)
  
      def _get_unused_gid_iscsi(self, port):
          group_range = self.configuration.hitachi_group_range
          if not group_range:
              group_range = basic_lib.DEFAULT_GROUP_RANGE
          return self.common.command.get_unused_gid_iscsi(group_range, port)
  
      def _delete_iscsi_target(self, port, target_no, target_alias):
          ret, _stdout, _stderr = self.common.command.delete_iscsi_target(
              port, target_no, target_alias)
          if ret:
              LOG.warning(basic_lib.set_msg(
                  307, port=port, tno=target_no, alias=target_alias))
  
      def _delete_chap_user(self, port):
          ret, _stdout, _stderr = self.common.command.delete_chap_user(port)
          if ret:
              LOG.warning(basic_lib.set_msg(
                  303, user=self.configuration.hitachi_auth_user))
  
      def _get_hostgroup_info_iscsi(self, hgs, host_iqn):
          return self.common.command.comm_get_hostgroup_info_iscsi(
              hgs, host_iqn, self.configuration.hitachi_target_ports)
  
      def _discovery_iscsi_target(self, hostgroups):
          for hostgroup in hostgroups:
              ip_addr, ip_port = self.common.command.comm_get_iscsi_ip(
                  hostgroup['port'])
              target_iqn = self.common.command.comm_get_target_iqn(
                  hostgroup['port'], hostgroup['gid'])
              hostgroup['ip_addr'] = ip_addr
              hostgroup['ip_port'] = ip_port
              hostgroup['target_iqn'] = target_iqn
              LOG.debug("ip_addr=%(addr)s ip_port=%(port)s target_iqn=%(iqn)s",
                        {'addr': ip_addr, 'port': ip_port, 'iqn': target_iqn})
  
      def _fill_groups(self, hgs, ports, target_iqn, target_alias, add_iqn):
          for port in ports:
              added_hostgroup = False
              added_user = False
              LOG.debug('Create target (hgs: %(hgs)s port: %(port)s '
                        'target_iqn: %(tiqn)s target_alias: %(alias)s '
                        'add_iqn: %(aiqn)s)',
                        {'hgs': hgs, 'port': port, 'tiqn': target_iqn,
                         'alias': target_alias, 'aiqn': add_iqn})
              gid = self.common.command.get_gid_from_targetiqn(
                  target_iqn, target_alias, port)
              if gid is None:
                  for retry_cnt in basic_lib.DEFAULT_TRY_RANGE:
                      gid = None
                      try:
                          gid = self._get_unused_gid_iscsi(port)
                          self.common.command.comm_add_hostgrp_iscsi(
                              port, gid, target_alias, target_iqn)
                          added_hostgroup = True
                      except exception.HBSDNotFound:
                          LOG.warning(basic_lib.set_msg(312, resource='GID'))
                          continue
                      except Exception as ex:
                          LOG.warning(basic_lib.set_msg(
                              309, port=port, alias=target_alias,
                              reason=ex))
                          break
                      else:
                          LOG.debug('Completed to add target'
                                    '(port: %(port)s gid: %(gid)d)',
                                    {'port': port, 'gid': gid})
                          break
              if gid is None:
                  LOG.error(_LE('Failed to add target(port: %s)'), port)
                  continue
              try:
                  if added_hostgroup:
                      if self.configuration.hitachi_auth_method:
                          added_user = self.common.command.set_chap_authention(
                              port, gid)
                      self.common.command.comm_set_hostgrp_reportportal(
                          port, target_alias)
                  self._add_initiator(hgs, port, gid, add_iqn)
              except Exception as ex:
                  LOG.warning(basic_lib.set_msg(
                      316, port=port, reason=ex))
                  if added_hostgroup:
                      if added_user:
                          self._delete_chap_user(port)
                      self._delete_iscsi_target(port, gid, target_alias)
  
      def add_hostgroup_core(self, hgs, ports, target_iqn,
                             target_alias, add_iqn):
          if ports:
              self._fill_groups(hgs, ports, target_iqn, target_alias, add_iqn)
  
      def add_hostgroup_master(self, hgs, master_iqn, host_ip, security_ports):
          target_ports = self.configuration.hitachi_target_ports
          group_request = self.configuration.hitachi_group_request
          target_alias = '%s%s' % (basic_lib.NAME_PREFIX, host_ip)
          if target_ports and group_request:
              target_iqn = '%s.target' % master_iqn
  
              diff_ports = []
              for port in security_ports:
                  for hostgroup in hgs:
                      if hostgroup['port'] == port:
                          break
                  else:
                      diff_ports.append(port)
  
              self.add_hostgroup_core(hgs, diff_ports, target_iqn,
                                      target_alias, master_iqn)
          if not hgs:
              raise exception.HBSDError(message=basic_lib.output_err(649))
  
      def add_hostgroup(self):
          properties = utils.brick_get_connector_properties()
          if 'initiator' not in properties:
              raise exception.HBSDError(
                  message=basic_lib.output_err(650, resource='HBA'))
          LOG.debug("initiator: %s", properties['initiator'])
          hostgroups = []
          security_ports = self._get_hostgroup_info_iscsi(
              hostgroups, properties['initiator'])
          self.add_hostgroup_master(hostgroups, properties['initiator'],
                                    properties['ip'], security_ports)
  
      def _get_properties(self, volume, hostgroups):
          conf = self.configuration
          properties = {}
          self._discovery_iscsi_target(hostgroups)
          hostgroup = hostgroups[0]
  
          properties['target_discovered'] = True
          properties['target_portal'] = "%s:%s" % (hostgroup['ip_addr'],
                                                   hostgroup['ip_port'])
          properties['target_iqn'] = hostgroup['target_iqn']
          properties['target_lun'] = hostgroup['lun']
  
          if conf.hitachi_auth_method:
              properties['auth_method'] = 'CHAP'
              properties['auth_username'] = conf.hitachi_auth_user
              properties['auth_password'] = conf.hitachi_auth_password
  
          return properties
  
      def do_setup(self, context):
          self.context = context
          self.common = common.HBSDCommon(self.configuration, self,
                                          context, self.db)
  
          self.check_param()
  
          self.common.create_lock_file()
  
          self.common.command.connect_storage()
  
          lock = basic_lib.get_process_lock(self.common.service_lock_file)
          with lock:
              self.add_hostgroup()
  
          self.output_param_to_log()
          self.do_setup_status.set()
  
      def check_for_setup_error(self):
          pass
  
      def extend_volume(self, volume, new_size):
          self.do_setup_status.wait()
          self.common.extend_volume(volume, new_size)
  
      def get_volume_stats(self, refresh=False):
          if refresh:
              if self.do_setup_status.isSet():
                  self.common.output_backend_available_once()
                  _stats = self.common.update_volume_stats("iSCSI")
                  if _stats:
                      self._stats = _stats
          return self._stats
  
      def create_volume(self, volume):
          self.do_setup_status.wait()
          metadata = self.common.create_volume(volume)
          return metadata
  
      def delete_volume(self, volume):
          self.do_setup_status.wait()
          self.common.delete_volume(volume)
  
      def create_snapshot(self, snapshot):
          self.do_setup_status.wait()
          metadata = self.common.create_snapshot(snapshot)
          return metadata
  
      def delete_snapshot(self, snapshot):
          self.do_setup_status.wait()
          self.common.delete_snapshot(snapshot)
  
      def create_cloned_volume(self, volume, src_vref):
          self.do_setup_status.wait()
          metadata = self.common.create_cloned_volume(volume, src_vref)
          return metadata
  
      def create_volume_from_snapshot(self, volume, snapshot):
          self.do_setup_status.wait()
          metadata = self.common.create_volume_from_snapshot(volume, snapshot)
          return metadata
  
      def _initialize_connection(self, ldev, connector, src_hgs=None):
          LOG.debug("Call _initialize_connection "
                    "(config_group: %(group)s ldev: %(ldev)d)",
                    {'group': self.configuration.config_group, 'ldev': ldev})
          if src_hgs:
              hostgroups = src_hgs[:]
          else:
              hostgroups = []
              security_ports = self._get_hostgroup_info_iscsi(
                  hostgroups, connector['initiator'])
              self.add_hostgroup_master(hostgroups, connector['initiator'],
                                        connector['ip'], security_ports)
  
          self._add_target(hostgroups, ldev)
  
          return hostgroups
  
      def initialize_connection(self, volume, connector):
          self.do_setup_status.wait()
          ldev = self.common.get_ldev(volume)
          if ldev is None:
              raise exception.HBSDError(
                  message=basic_lib.output_err(619, volume_id=volume['id']))
          self.common.add_volinfo(ldev, volume['id'])
          with self.common.volume_info[ldev]['lock'],\
                  self.common.volume_info[ldev]['in_use']:
              hostgroups = self._initialize_connection(ldev, connector)
              protocol = 'iscsi'
              properties = self._get_properties(volume, hostgroups)
              LOG.debug('Initialize volume_info: %s',
                        self.common.volume_info)
  
          LOG.debug('HFCDrv: properties=%s', properties)
          return {
              'driver_volume_type': protocol,
              'data': properties
          }
  
      def _terminate_connection(self, ldev, connector, src_hgs):
          LOG.debug("Call _terminate_connection(config_group: %s)",
                    self.configuration.config_group)
          hostgroups = src_hgs[:]
          self._delete_lun_iscsi(hostgroups, ldev)
  
          LOG.debug("*** _terminate_ ***")
  
      def terminate_connection(self, volume, connector, **kwargs):
          self.do_setup_status.wait()
          ldev = self.common.get_ldev(volume)
          if ldev is None:
              LOG.warning(basic_lib.set_msg(302, volume_id=volume['id']))
              return
  
          if 'initiator' not in connector:
              raise exception.HBSDError(
                  message=basic_lib.output_err(650, resource='HBA'))
  
          hostgroups = []
          self._get_hostgroup_info_iscsi(hostgroups,
                                         connector['initiator'])
          if not hostgroups:
              raise exception.HBSDError(message=basic_lib.output_err(649))
  
          self.common.add_volinfo(ldev, volume['id'])
          with self.common.volume_info[ldev]['lock'],\
                  self.common.volume_info[ldev]['in_use']:
              self._terminate_connection(ldev, connector, hostgroups)
  
      def create_export(self, context, volume):
          pass
  
      def ensure_export(self, context, volume):
          pass
  
      def remove_export(self, context, volume):
          pass
  
      def pair_initialize_connection(self, unused_ldev):
          pass
  
      def pair_terminate_connection(self, unused_ldev):
          pass
  
      def copy_volume_to_image(self, context, volume, image_service, image_meta):
          self.do_setup_status.wait()
          if volume['volume_attachment']:
              desc = 'volume %s' % volume['id']
              raise exception.HBSDError(
                  message=basic_lib.output_err(660, desc=desc))
          super(HBSDISCSIDriver, self).copy_volume_to_image(context, volume,
                                                            image_service,
                                                            image_meta)
  
      def manage_existing(self, volume, existing_ref):
          return self.common.manage_existing(volume, existing_ref)
  
      def manage_existing_get_size(self, volume, existing_ref):
          self.do_setup_status.wait()
          return self.common.manage_existing_get_size(volume, existing_ref)
  
      def unmanage(self, volume):
          self.do_setup_status.wait()
          self.common.unmanage(volume)
  