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

"""
Hitachi Unified Storage (HUS-HNAS) platform. Backend operations.
"""

import re

from oslo_concurrency import processutils as putils
from oslo_log import log as logging
from oslo_utils import units
import six

from cinder.i18n import _, _LW, _LI, _LE
from cinder import exception
from cinder import ssh_utils
from cinder import utils

LOG = logging.getLogger("cinder.volume.driver")
HNAS_SSC_RETRIES = 5


class HnasBackend(object):
    """Back end. Talks to HUS-HNAS."""
    def __init__(self, drv_configs):
        self.drv_configs = drv_configs
        self.sshpool = None

    @utils.retry(exceptions=exception.HNASConnError, retries=HNAS_SSC_RETRIES,
                 wait_random=True)
    def run_cmd(self, cmd, ip0, user, pw, *args, **kwargs):
        """Run a command on SMU or using SSH

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :returns: formated string with version information
        """
        LOG.debug('Enable ssh: %s',
                  six.text_type(self.drv_configs['ssh_enabled']))

        if self.drv_configs['ssh_enabled'] != 'True':
            # Direct connection via ssc
            args = (cmd, '--user', user, '--password', pw, ip0) + args

            try:
                out, err = utils.execute(*args, **kwargs)
                LOG.debug("command %(cmd)s result: out = %(out)s - err = "
                          "%(err)s", {'cmd': cmd, 'out': out, 'err': err})
                return out, err
            except putils.ProcessExecutionError as e:
                if 'Failed to establish SSC connection' in e.stderr:
                    LOG.debug("SSC connection error!")
                    msg = _("Failed to establish SSC connection.")
                    raise exception.HNASConnError(msg)
                elif 'Connection reset' in e.stderr:
                    LOG.debug("HNAS connection reset!")
                    msg = _("HNAS has disconnected SSC")
                    raise exception.HNASConnError(msg)
                else:
                    raise
        else:
            if self.drv_configs['cluster_admin_ip0'] is None:
                # Connect to SMU through SSH and run ssc locally
                args = (cmd, 'localhost') + args
            else:
                args = (cmd, '--smuauth',
                        self.drv_configs['cluster_admin_ip0']) + args

            utils.check_ssh_injection(args)
            command = ' '.join(args)
            command = command.replace('"', '\\"')

            if not self.sshpool:
                server = self.drv_configs['mgmt_ip0']
                port = int(self.drv_configs['ssh_port'])
                username = self.drv_configs['username']
                # We only accept private/public key auth
                password = ""
                privatekey = self.drv_configs['ssh_private_key']
                self.sshpool = ssh_utils.SSHPool(server,
                                                 port,
                                                 None,
                                                 username,
                                                 password=password,
                                                 privatekey=privatekey)

            with self.sshpool.item() as ssh:

                try:
                    out, err = putils.ssh_execute(ssh, command,
                                                  check_exit_code=True)
                    LOG.debug("command %(cmd)s result: out = "
                              "%(out)s - err = %(err)s",
                              {'cmd': cmd, 'out': out, 'err': err})
                    return out, err
                except putils.ProcessExecutionError as e:
                    if 'Failed to establish SSC connection' in e.stderr:
                        LOG.debug("SSC connection error!")
                        msg = _("Failed to establish SSC connection.")
                        raise exception.HNASConnError(msg)
                    else:
                        raise putils.ProcessExecutionError

    def get_version(self, cmd, ver, ip0, user, pw):
        """Gets version information from the storage unit

       :param cmd: ssc command name
       :param ver: string driver version
       :param ip0: string IP address of controller
       :param user: string user authentication for array
       :param pw: string password authentication for array
       :returns: formatted string with version information
       """
        out, err = self.run_cmd(cmd, ip0, user, pw, "cluster-getmac",
                                check_exit_code=True)
        hardware = out.split()[2]

        out, err = self.run_cmd(cmd, ip0, user, pw, "ver",
                                check_exit_code=True)
        lines = out.split('\n')

        model = ""
        for line in lines:
            if 'Model:' in line:
                model = line.split()[1]
            if 'Software:' in line:
                ver = line.split()[1]

        # If not using SSH, the local utility version can be different from the
        # one used in HNAS
        if self.drv_configs['ssh_enabled'] != 'True':
            out, err = utils.execute(cmd, "-version", check_exit_code=True)
            util = out.split()[1]

            out = ("Array_ID: %(arr)s (%(mod)s) version: %(ver)s LU: 256 "
                   "RG: 0 RG_LU: 0 Utility_version: %(util)s" %
                   {'arr': hardware, 'mod': model, 'ver': ver, 'util': util})
        else:
            out = ("Array_ID: %(arr)s (%(mod)s) version: %(ver)s LU: 256 "
                   "RG: 0 RG_LU: 0" %
                   {'arr': hardware, 'mod': model, 'ver': ver})

        LOG.debug('get_version: %(out)s -- %(err)s', {'out': out, 'err': err})
        return out

    def get_iscsi_info(self, cmd, ip0, user, pw):
        """Gets IP addresses for EVSs, use EVSID as controller.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :returns: formated string with iSCSI information
        """

        out, err = self.run_cmd(cmd, ip0, user, pw,
                                'evsipaddr', '-l',
                                check_exit_code=True)
        lines = out.split('\n')

        newout = ""
        for line in lines:
            if 'evs' in line and 'admin' not in line:
                inf = line.split()
                (evsnum, ip) = (inf[1], inf[3])
                newout += "CTL: %s Port: 0 IP: %s Port: 3260 Link: Up\n" \
                          % (evsnum, ip)

        LOG.debug('get_iscsi_info: %(out)s -- %(err)s',
                  {'out': out, 'err': err})
        return newout

    def get_hdp_info(self, cmd, ip0, user, pw, fslabel=None):
        """Gets the list of filesystems and fsids.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param fslabel: filesystem label we want to get info
        :returns: formated string with filesystems and fsids
        """

        if fslabel is None:
            out, err = self.run_cmd(cmd, ip0, user, pw, 'df', '-a',
                                    check_exit_code=True)
        else:
            out, err = self.run_cmd(cmd, ip0, user, pw, 'df', '-f', fslabel,
                                    check_exit_code=True)

        lines = out.split('\n')
        single_evs = True

        LOG.debug("Parsing output: %s", lines)

        newout = ""
        for line in lines:
            if 'Not mounted' in line or 'Not determined' in line:
                continue
            if 'not' not in line and 'EVS' in line:
                single_evs = False
            if 'GB' in line or 'TB' in line:
                LOG.debug("Parsing output: %s", line)
                inf = line.split()

                if not single_evs:
                    (fsid, fslabel, capacity) = (inf[0], inf[1], inf[3])
                    (used, perstr) = (inf[5], inf[7])
                    (availunit, usedunit) = (inf[4], inf[6])
                else:
                    (fsid, fslabel, capacity) = (inf[0], inf[1], inf[2])
                    (used, perstr) = (inf[4], inf[6])
                    (availunit, usedunit) = (inf[3], inf[5])

                if usedunit == 'GB':
                    usedmultiplier = units.Ki
                else:
                    usedmultiplier = units.Mi
                if availunit == 'GB':
                    availmultiplier = units.Ki
                else:
                    availmultiplier = units.Mi
                m = re.match("\((\d+)\%\)", perstr)
                if m:
                    percent = m.group(1)
                else:
                    percent = 0
                newout += "HDP: %s %d MB %d MB %d %% LUs: 256 Normal %s\n" \
                          % (fsid, int(float(capacity) * availmultiplier),
                             int(float(used) * usedmultiplier),
                             int(percent), fslabel)

        LOG.debug('get_hdp_info: %(out)s -- %(err)s',
                  {'out': newout, 'err': err})
        return newout

    def get_evs(self, cmd, ip0, user, pw, fsid):
        """Gets the EVSID for the named filesystem.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :returns: EVS id of the file system
        """

        out, err = self.run_cmd(cmd, ip0, user, pw, "evsfs", "list",
                                check_exit_code=True)
        LOG.debug('get_evs: out %s.', out)

        lines = out.split('\n')
        for line in lines:
            inf = line.split()
            if fsid in line and (fsid == inf[0] or fsid == inf[1]):
                return inf[3]

        LOG.warning(_LW('get_evs: %(out)s -- No find for %(fsid)s'),
                    {'out': out, 'fsid': fsid})
        return 0

    def _get_evsips(self, cmd, ip0, user, pw, evsid):
        """Gets the EVS IPs for the named filesystem."""

        out, err = self.run_cmd(cmd, ip0, user, pw,
                                'evsipaddr', '-e', evsid,
                                check_exit_code=True)

        iplist = ""
        lines = out.split('\n')
        for line in lines:
            inf = line.split()
            if 'evs' in line:
                iplist += inf[3] + ' '

        LOG.debug('get_evsips: %s', iplist)
        return iplist

    def _get_fsid(self, cmd, ip0, user, pw, fslabel):
        """Gets the FSID for the named filesystem."""

        out, err = self.run_cmd(cmd, ip0, user, pw, 'evsfs', 'list',
                                check_exit_code=True)
        LOG.debug('get_fsid: out %s', out)

        lines = out.split('\n')
        for line in lines:
            inf = line.split()
            if fslabel in line and fslabel == inf[1]:
                LOG.debug('get_fsid: %s', line)
                return inf[0]

        LOG.warning(_LW('get_fsid: %(out)s -- No info for %(fslabel)s'),
                    {'out': out, 'fslabel': fslabel})
        return 0

    def _get_targets(self, cmd, ip0, user, pw, evsid, tgtalias=None):
        """Get the target list of an EVS.

        Get the target list of an EVS. Optionally can return the target
        list of a specific target.
        """

        LOG.debug("Getting target list for evs %s, tgtalias: %s.",
                  evsid, tgtalias)

        try:
            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                    "--evs", evsid, 'iscsi-target', 'list',
                                    check_exit_code=True)
        except putils.ProcessExecutionError as e:
            LOG.error(_LE('Error getting iSCSI target info '
                          'from EVS %(evs)s.'), {'evs': evsid})
            LOG.debug("_get_targets out: %(out)s, err: %(err)s.",
                      {'out': e.stdout, 'err': e.stderr})
            return []

        tgt_list = []
        if 'No targets' in out:
            LOG.debug("No targets found in EVS %(evsid)s.", {'evsid': evsid})
            return tgt_list

        tgt_raw_list = out.split('Alias')[1:]
        for tgt_raw_info in tgt_raw_list:
            tgt = {}
            tgt['alias'] = tgt_raw_info.split('\n')[0].split(' ').pop()
            tgt['iqn'] = tgt_raw_info.split('\n')[1].split(' ').pop()
            tgt['secret'] = tgt_raw_info.split('\n')[3].split(' ').pop()
            tgt['auth'] = tgt_raw_info.split('\n')[4].split(' ').pop()
            luns = []
            tgt_raw_info = tgt_raw_info.split('\n\n')[1]
            tgt_raw_list = tgt_raw_info.split('\n')[2:]

            for lun_raw_line in tgt_raw_list:
                lun_raw_line = lun_raw_line.strip()
                lun_raw_line = lun_raw_line.split(' ')
                lun = {}
                lun['id'] = lun_raw_line[0]
                lun['name'] = lun_raw_line.pop()
                luns.append(lun)

            tgt['luns'] = luns

            if tgtalias == tgt['alias']:
                return [tgt]

            tgt_list.append(tgt)

        if tgtalias is not None:
            # We tried to find  'tgtalias' but didn't find. Return an empty
            # list.
            LOG.debug("There's no target %(alias)s in EVS %(evsid)s.",
                      {'alias': tgtalias, 'evsid': evsid})
            return []

        LOG.debug("Targets in EVS %(evs)s: %(tgtl)s.",
                  {'evs': evsid, 'tgtl': tgt_list})
        return tgt_list

    def _get_unused_lunid(self, cmd, ip0, user, pw, tgt_info):

        if len(tgt_info['luns']) == 0:
            return 0

        free_lun = 0
        for lun in tgt_info['luns']:
            if int(lun['id']) == free_lun:
                free_lun += 1

            if int(lun['id']) > free_lun:
                # Found a free LUN number
                break

        return free_lun

    def get_nfs_info(self, cmd, ip0, user, pw):
        """Gets information on each NFS export.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :returns: formated string
        """

        out, err = self.run_cmd(cmd, ip0, user, pw,
                                'for-each-evs', '-q',
                                'nfs-export', 'list',
                                check_exit_code=True)

        lines = out.split('\n')
        newout = ""
        export = ""
        path = ""
        for line in lines:
            inf = line.split()
            if 'Export name' in line:
                export = inf[2]
            if 'Export path' in line:
                path = inf[2]
            if 'File system info' in line:
                fs = ""
            if 'File system label' in line:
                fs = inf[3]
            if 'Transfer setting' in line and fs != "":
                fsid = self._get_fsid(cmd, ip0, user, pw, fs)
                evsid = self.get_evs(cmd, ip0, user, pw, fsid)
                ips = self._get_evsips(cmd, ip0, user, pw, evsid)
                newout += "Export: %s Path: %s HDP: %s FSID: %s \
                           EVS: %s IPS: %s\n" \
                           % (export, path, fs, fsid, evsid, ips)
                fs = ""

        LOG.debug('get_nfs_info: %(out)s -- %(err)s',
                  {'out': newout, 'err': err})
        return newout

    def create_lu(self, cmd, ip0, user, pw, hdp, size, name):
        """Creates a new Logical Unit.

        If the operation can not be performed for some reason, utils.execute()
        throws an error and aborts the operation. Used for iSCSI only

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param hdp: data Pool the logical unit will be created
        :param size: Size (Mb) of the new logical unit
        :param name: name of the logical unit
        :returns: formated string with 'LUN %d HDP: %d size: %s MB, is
                  successfully created'
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-lu', 'add', "-e",
                                name, hdp,
                                '/.cinder/' + name + '.iscsi',
                                size + 'M',
                                check_exit_code=True)

        out = "LUN %s HDP: %s size: %s MB, is successfully created" \
              % (name, hdp, size)

        LOG.debug('create_lu: %s.', out)
        return out

    def delete_lu(self, cmd, ip0, user, pw, hdp, lun):
        """Delete an logical unit. Used for iSCSI only

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param hdp: data Pool of the logical unit
        :param lun: id of the logical unit being deleted
        :returns: formated string 'Logical unit deleted successfully.'
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-lu', 'del', '-d',
                                '-f', lun,
                                check_exit_code=True)

        LOG.debug('delete_lu: %(out)s -- %(err)s.', {'out': out, 'err': err})
        return out

    def create_dup(self, cmd, ip0, user, pw, src_lun, hdp, size, name):
        """Clones a volume

        Clone primitive used to support all iSCSI snapshot/cloning functions.
        Used for iSCSI only.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param hdp: data Pool of the logical unit
        :param src_lun: id of the logical unit being deleted
        :param size: size of the LU being cloned. Only for logging purposes
        :returns: formated string
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-lu', 'clone', '-e',
                                src_lun, name,
                                '/.cinder/' + name + '.iscsi',
                                check_exit_code=True)

        out = "LUN %s HDP: %s size: %s MB, is successfully created" \
              % (name, hdp, size)

        LOG.debug('create_dup: %(out)s -- %(err)s.', {'out': out, 'err': err})
        return out

    def file_clone(self, cmd, ip0, user, pw, fslabel, src, name):
        """Clones NFS files to a new one named 'name'

        Clone primitive used to support all NFS snapshot/cloning functions.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param fslabel:  file system label of the new file
        :param src: source file
        :param name: target path of the new created file
        :returns: formated string
        """

        _fsid = self._get_fsid(cmd, ip0, user, pw, fslabel)
        _evsid = self.get_evs(cmd, ip0, user, pw, _fsid)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'file-clone-create', '-f', fslabel,
                                src, name,
                                check_exit_code=True)

        out = "LUN %s HDP: %s Clone: %s -> %s" % (name, _fsid, src, name)

        LOG.debug('file_clone: %(out)s -- %(err)s.', {'out': out, 'err': err})
        return out

    def extend_vol(self, cmd, ip0, user, pw, hdp, lun, new_size, name):
        """Extend a iSCSI volume.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param hdp: data Pool of the logical unit
        :param lun: id of the logical unit being extended
        :param new_size: new size of the LU
        :param name: formated string
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-lu', 'expand',
                                name, new_size + 'M',
                                check_exit_code=True)

        out = ("LUN: %s successfully extended to %s MB" % (name, new_size))

        LOG.debug('extend_vol: %s.', out)
        return out

    @utils.retry(putils.ProcessExecutionError, retries=HNAS_SSC_RETRIES,
                 wait_random=True)
    def add_iscsi_conn(self, cmd, ip0, user, pw, lun_name, hdp,
                       port, tgtalias, initiator):
        """Setup the lun on on the specified target port

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param lun_name: id of the logical unit being extended
        :param hdp: data pool of the logical unit
        :param port: iSCSI port
        :param tgtalias: iSCSI qualified name
        :param initiator: initiator address
        """

        LOG.debug('Adding %(lun)s to %(tgt)s returns %(tgt)s.',
                  {'lun': lun_name, 'tgt': tgtalias})
        found, lunid, tgt = self.check_lu(cmd, ip0, user, pw, lun_name, hdp)
        evsid = self.get_evs(cmd, ip0, user, pw, hdp)

        if found:
            conn = (int(lunid), lun_name, initiator, int(lunid), tgt['iqn'],
                    int(lunid), hdp, port)
            out = ("H-LUN: %d mapped LUN: %s, iSCSI Initiator: %s "
                   "@ index: %d, and Target: %s @ index %d is "
                   "successfully paired  @ CTL: %s, Port: %s.") % conn
        else:
            tgt = self._get_targets(cmd, ip0, user, pw, evsid, tgtalias)
            lunid = self._get_unused_lunid(cmd, ip0, user, pw, tgt[0])

            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                    "--evs", evsid,
                                    'iscsi-target', 'addlu',
                                    tgtalias, lun_name, six.text_type(lunid),
                                    check_exit_code=True)

            conn = (int(lunid), lun_name, initiator, int(lunid), tgt[0]['iqn'],
                    int(lunid), hdp, port)
            out = ("H-LUN: %d mapped LUN: %s, iSCSI Initiator: %s "
                   "@ index: %d, and Target: %s @ index %d is "
                   "successfully paired  @ CTL: %s, Port: %s.") % conn

        LOG.debug('add_iscsi_conn: returns %s.', out)
        return out

    def del_iscsi_conn(self, cmd, ip0, user, pw, evsid, iqn, hlun):
        """Remove the lun on on the specified target port

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param evsid: EVSID for the file system
        :param iqn: iSCSI qualified name
        :param hlun: logical unit id
        :returns: formated string
        """

        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", evsid,
                                'iscsi-target', 'list', iqn,
                                check_exit_code=True)

        lines = out.split('\n')
        out = ("H-LUN: %d already deleted from target %s" % (int(hlun), iqn))
        # see if lun is already detached
        for line in lines:
            if line.startswith('  '):
                lunline = line.split()[0]
                if lunline[0].isdigit() and lunline == hlun:
                    out = ""
                    break

        if out != "":
            # hlun wasn't found
            LOG.info(_LI('del_iscsi_conn: hlun not found %s.'), out)
            return out

        # remove the LU from the target
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", evsid,
                                'iscsi-target', 'dellu',
                                '-f', iqn, hlun,
                                check_exit_code=True)

        out = "H-LUN: %d successfully deleted from target %s" \
              % (int(hlun), iqn)

        LOG.debug('del_iscsi_conn: %s.', out)
        return out

    def get_targetiqn(self, cmd, ip0, user, pw, targetalias, hdp, secret):
        """Obtain the targets full iqn

        Returns the target's full iqn rather than its alias.
        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param targetalias: alias of the target
        :param hdp: data pool of the logical unit
        :param secret: CHAP secret of the target
        :returns: string with full IQN
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-target', 'list', targetalias,
                                check_exit_code=True)

        if "does not exist" in out:
            if secret == "":
                secret = '""'
                out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                        "--evs", _evsid,
                                        'iscsi-target', 'add',
                                        targetalias, secret,
                                        check_exit_code=True)
            else:
                out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                        "--evs", _evsid,
                                        'iscsi-target', 'add',
                                        targetalias, secret,
                                        check_exit_code=True)
            if "success" in out:
                return targetalias

        lines = out.split('\n')
        # returns the first iqn
        for line in lines:
            if 'Alias' in line:
                fulliqn = line.split()[2]
                return fulliqn

    def set_targetsecret(self, cmd, ip0, user, pw, targetalias, hdp, secret):
        """Sets the chap secret for the specified target.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param targetalias: alias of the target
        :param hdp: data pool of the logical unit
        :param secret: CHAP secret of the target
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-target', 'list',
                                targetalias,
                                check_exit_code=False)

        if "does not exist" in out:
            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                    "--evs", _evsid,
                                    'iscsi-target', 'add',
                                    targetalias, secret,
                                    check_exit_code=True)
        else:
            LOG.info(_LI('targetlist: %s'), targetalias)
            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                    "--evs", _evsid,
                                    'iscsi-target', 'mod',
                                    '-s', secret, '-a', 'enable',
                                    targetalias,
                                    check_exit_code=True)

    def get_targetsecret(self, cmd, ip0, user, pw, targetalias, hdp):
        """Returns the chap secret for the specified target.

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param targetalias: alias of the target
        :param hdp: data pool of the logical unit
        :return secret: CHAP secret of the target
        """

        _evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
                                "--evs", _evsid,
                                'iscsi-target', 'list', targetalias,
                                check_exit_code=True)

        enabled = ""
        secret = ""
        lines = out.split('\n')
        for line in lines:
            if 'Secret' in line:
                if len(line.split()) > 2:
                    secret = line.split()[2]
            if 'Authentication' in line:
                enabled = line.split()[2]

        if enabled == 'Enabled':
            return secret
        else:
            return ""

    def check_target(self, cmd, ip0, user, pw, hdp, target_alias):
        """Checks if a given target exists and gets its info

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param hdp: pool name used
        :param target_alias: alias of the target
        :returns: True if target exists
        :returns: list with the target info
        """

        LOG.debug("Checking if target %(tgt)s exists.", {'tgt': target_alias})
        evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        tgt_list = self._get_targets(cmd, ip0, user, pw, evsid)

        for tgt in tgt_list:
            if tgt['alias'] == target_alias:
                attached_luns = len(tgt['luns'])
                LOG.debug("Target %(tgt)s has %(lun)s volumes.",
                          {'tgt': target_alias, 'lun': attached_luns})
                return True, tgt

        LOG.debug("Target %(tgt)s does not exist.", {'tgt': target_alias})
        return False, None

    def check_lu(self, cmd, ip0, user, pw, volume_name, hdp):
        """Checks if a given LUN is already mapped

        :param cmd: ssc command name
        :param ip0: string IP address of controller
        :param user: string user authentication for array
        :param pw: string password authentication for array
        :param volume_name: number of the LUN
        :param hdp: storage pool of the LUN
        :returns: True if the lun is attached
        :returns: the LUN id
        :returns: Info related to the target
        """

        LOG.debug("Checking if vol %s (hdp: %s) is attached.",
                  volume_name, hdp)
        evsid = self.get_evs(cmd, ip0, user, pw, hdp)
        tgt_list = self._get_targets(cmd, ip0, user, pw, evsid)

        for tgt in tgt_list:
            if len(tgt['luns']) == 0:
                continue

            for lun in tgt['luns']:
                lunid = lun['id']
                lunname = lun['name']
                if lunname[:29] == volume_name[:29]:
                    LOG.debug("LUN %(lun)s attached on %(lunid)s, "
                              "target: %(tgt)s.",
                              {'lun': volume_name, 'lunid': lunid, 'tgt': tgt})
                    return True, lunid, tgt

        LOG.debug("LUN %(lun)s not attached.", {'lun': volume_name})
        return False, 0, None

    def get_existing_lu_info(self, cmd, ip0, user, pw, fslabel, lun):
        """Returns the information for the specified Logical Unit.

        Returns the information of an existing Logical Unit on HNAS, according
        to the name provided.

        :param cmd:     the command that will be run on SMU
        :param ip0:     string IP address of controller
        :param user:    string user authentication for array
        :param pw:      string password authentication for array
        :param fslabel: label of the file system
        :param lun:     label of the logical unit
        """

        evs = self.get_evs(cmd, ip0, user, pw, fslabel)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context", "--evs",
                                evs, 'iscsi-lu', 'list', lun)

        return out

    def rename_existing_lu(self, cmd, ip0, user, pw, fslabel,
                           new_name, vol_name):
        """Renames the specified Logical Unit.

         Renames an existing Logical Unit on HNAS according to the new name
         provided.

        :param cmd:      command that will be run on SMU
        :param ip0:      string IP address of controller
        :param user:     string user authentication for array
        :param pw:       string password authentication for array
        :param fslabel:  label of the file system
        :param new_name: new name to the existing volume
        :param vol_name: current name of the existing volume
        """
        evs = self.get_evs(cmd, ip0, user, pw, fslabel)
        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context", "--evs",
                                evs, "iscsi-lu", "mod", "-n", new_name,
                                vol_name)

        return out
