# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
#
#    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.

"""Generic linux scsi subsystem and Multipath utilities.

   Note, this is not iSCSI.
"""
import os
import re

from oslo_concurrency import processutils as putils
from oslo_log import log as logging

from cinder.brick import exception
from cinder.brick import executor
from cinder.i18n import _LW, _LE
from cinder.openstack.common import loopingcall

LOG = logging.getLogger(__name__)

MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$")
MULTIPATH_WWID_REGEX = re.compile("\((?P<wwid>.+)\)")


class LinuxSCSI(executor.Executor):
    def __init__(self, root_helper, execute=putils.execute,
                 *args, **kwargs):
        super(LinuxSCSI, self).__init__(root_helper, execute,
                                        *args, **kwargs)

    def echo_scsi_command(self, path, content):
        """Used to echo strings to scsi subsystem."""

        args = ["-a", path]
        kwargs = dict(process_input=content,
                      run_as_root=True,
                      root_helper=self._root_helper)
        self._execute('tee', *args, **kwargs)

    def get_name_from_path(self, path):
        """Translates /dev/disk/by-path/ entry to /dev/sdX."""

        name = os.path.realpath(path)
        if name.startswith("/dev/"):
            return name
        else:
            return None

    def remove_scsi_device(self, device):
        """Removes a scsi device based upon /dev/sdX name."""

        path = "/sys/block/%s/device/delete" % device.replace("/dev/", "")
        if os.path.exists(path):
            # flush any outstanding IO first
            self.flush_device_io(device)

            LOG.debug("Remove SCSI device(%(dev)s) with %(path)s",
                      {'dev': device, 'path': path})
            self.echo_scsi_command(path, "1")

    def wait_for_volume_removal(self, volume_path):
        """This is used to ensure that volumes are gone."""

        def _wait_for_volume_removal(volume_path):
            LOG.debug("Waiting for SCSI mount point %s to be removed.",
                      volume_path)
            if os.path.exists(volume_path):
                if self.tries >= self.scan_attempts:
                    LOG.error(_LE("Exceeded the number of attempts to detect "
                                  "volume removal."))
                    raise exception.VolumePathNotRemoved(
                        volume_path=volume_path)

                LOG.debug("%(path)s still exists, rescanning. Try number: "
                          "%(tries)s",
                          {'path': volume_path, 'tries': self.tries})
                self.tries = self.tries + 1
            else:
                LOG.debug("SCSI mount point %s has been removed.", volume_path)
                raise loopingcall.LoopingCallDone()