# Copyright (c) 2013 Hitachi Data Systems, Inc.
# Copyright (c) 2013 OpenStack Foundation
# 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.
#

"""
iSCSI Cinder Volume driver for Hitachi Unified Storage (HUS) platform.
"""

from xml.etree import ElementTree as ETree

from oslo.utils import excutils
from oslo_config import cfg

from cinder import exception
from cinder.i18n import _, _LE, _LI
from cinder.openstack.common import log as logging
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.hds.hus_backend import HusBackend

HDS_VERSION = '1.0.2'

LOG = logging.getLogger(__name__)

HUS_OPTS = [
    cfg.StrOpt('hds_cinder_config_file',
               default='/opt/hds/hus/cinder_hus_conf.xml',
               help='The configuration file for the Cinder HDS driver '
                    'for HUS'), ]

CONF = cfg.CONF
CONF.register_opts(HUS_OPTS)

HI_IQN = 'iqn.1994-04.jp.co.hitachi:'  # fixed string, for now.

HUS_DEFAULT_CONFIG = {'hus_cmd': 'hus-cmd',
                      'lun_start': '0',
                      'lun_end': '8192'}


def factory_bend():
    """Factory over-ride in self-tests."""
    return HusBackend()


def _loc_info(loc):
    """Parse info from location string."""
    info = {}
    tup = loc.split(',')
    if len(tup) < 5:
        info['id_lu'] = tup[0].split('.')
        return info
    info['id_lu'] = tup[2].split('.')
    info['tgt'] = tup
    return info


def _do_lu_range_check(start, end, maxlun):
    """Validate array allocation range."""
    LOG.debug("Range: start LU: %(start)s, end LU: %(end)s"
              % {'start': start,
                 'end': end})
    if int(start) < 0:
        msg = 'start LU limit too low: ' + start
        raise exception.InvalidInput(reason=msg)
    if int(start) >= int(maxlun):
        msg = 'start LU limit high: ' + start + ' max: ' + maxlun
        raise exception.InvalidInput(reason=msg)
    if int(end) <= int(start):
        msg = 'LU end limit too low: ' + end
        raise exception.InvalidInput(reason=msg)
    if int(end) > int(maxlun):
        end = maxlun
        LOG.debug("setting LU upper (end) limit to %s" % maxlun)
    return (start, end)


def _xml_read(root, element, check=None):
    """Read an xml element."""
    try:
        val = root.findtext(element)
        LOG.info(_LI("%(element)s: %(val)s")
                 % {'element': element,
                    'val': val})
        if val:
            return val.strip()
        if check:
            raise exception.ParameterNotFound(param=element)
        return None
    except ETree.ParseError:
        if check:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE("XML exception reading parameter: %s") % element)
        else:
            LOG.info(_LI("XML exception reading parameter: %s") % element)
            return None


def _read_config(xml_config_file):
    """Read hds driver specific xml config file."""
    try:
        root = ETree.parse(xml_config_file).getroot()
    except Exception:
        raise exception.NotFound(message='config file not found: '
                                 + xml_config_file)
    config = {}
    arg_prereqs = ['mgmt_ip0', 'mgmt_ip1', 'username', 'password']
    for req in arg_prereqs:
        config[req] = _xml_read(root, req, 'check')

    config['hdp'] = {}
    config['services'] = {}
    for svc in ['svc_0', 'svc_1', 'svc_2', 'svc_3']:  # min one needed
     
   if _xml_read(root, svc) is None:
            continue
        service = {}
        service['label'] = svc
        for arg in ['volume_type', 'hdp', 'iscsi_ip']:  # none optional
            service[arg] = _xml_read(root, svc + '/' + arg, 'check')
        config['services'][service['volume_type']] = service
        config['hdp'][service['hdp']] = service['hdp']

    if config['services'].keys() is None:  # at least one service required!
        raise exceptio
n.ParameterNotFound(param="No service found")

    config['snapshot_hdp'] = _xml_read(root, 'snapshot/hdp', 'check')

    for arg in ['hus_cmd', 'lun_start', 'lun_end']:  # optional
        config[arg] = _xml_read(root, arg) or HUS_DEFAULT_CONFIG[arg]

    return config


class HUSDriver(driver.ISCSIDriver):
    """HDS HUS volume driver."""

    VERSION = HDS_VERSION

    def _array_info_get(self):
        """Get array parameters."""
        out = self.bend.get_version(self.config['hus_cmd'],
                                    HDS_VERSION,
                                    self.config['mgmt_ip0'],
                                    self.config['mgmt_ip1'],
                                    self.config['username'],
                                    self.config['password'])
        inf = out.split()
        return(inf[1], 'hus_' + inf[1], inf[6])

    def _get_iscsi_info(self):
        """Validate array iscsi parameters."""
        out = self.bend.get_iscsi_info(self.config['hus_cmd'],
                                       HDS_VERSION,
                                       self.config['mgmt_ip0'],
                                       self.config['mgmt_ip1'],
                                       self.config['username'],
                                       self.config['password'])
        lines = out.split('\n')
        conf = {}                 # dict based on iSCSI portal ip addresses
        for line in lines:
            if 'CTL' in line:
                inf = line.split()
                (ctl, port, ip, ipp) = (inf[1], inf[3], inf[5], inf[7])
                conf[ip] = {}
                conf[ip]['ctl'] = ctl
                conf[ip]['port'] = port
                conf[ip]['iscsi_port'] = ipp  # HUS default: 3260
                msg = _('portal: %(ip)s:%(ipp)s, CTL: %(ctl)s, port: %(port)s')
                LOG.debug(msg
                          % {'ip': ip,
                             'ipp': ipp,
                             'ctl': ctl,
                             'port': port})
        return conf

    def _get_service(self, volume):
        """Get the available service parameters for a given volume type."""
        label = None
        if volume['volume_type']:
            label = volume['volume_type']['name']
        label = label or 'default'
        if label in self.config['services'].keys():
            svc = self.config['services'][label]
            service = (svc['iscsi_ip'], svc['iscsi_port'], svc['ctl'],
                       svc['port'], svc['hdp'])  # ip, ipp, ctl, port, hdp
        else:
            LOG.error(_LE("No configuration found for service: %s") % label)
            raise exception.ParameterNotFound(param=label)
        return service

    def _get_stats(self):
        """Get HDP stats from HUS."""
        total_cap = 0
        total_used = 0
        out = self.bend.get_hdp_info(self.config['hus_cmd'],
                                     HDS_VERSION,
                                     self.config['mgmt_ip0'],
                                     self.config['mgmt_ip1'],
                                     self.config['username'],
                                     self.config['password'])
        for line in out.split('\n'):
            if 'HDP' in line:
                (hdp, size, _ign, used) = line.split()[1:5]  # in MB
                if hdp in self.config['hdp'].keys():
                    total_cap += int(size)
                    total_used += int(used)
        hus_stat = {}
        hus_stat['total_capacity_gb'] = int(total_cap / 1024)  # in GB
        hus_stat['free_capacity_gb'] = int((total_cap - total_used) / 1024)
        be_name = self.configuration.safe_get('volume_backend_name')
        hus_s