##
# Copyright 2017 Pure Storage Inc
#
# 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.
"""

Management of Pure Storage FlashArray

Installation Prerequisites
--------------------------
- You will need the ``purestorage`` python package in your python installation
  path that is running salt.

  .. code-block:: bash

      pip install purestorage

- Configure Pure Storage FlashArray authentication. Use one of the following
  three methods.

  1) From the minion config

  .. code-block:: yaml

        pure_tags:
          fa:
            san_ip: management vip or hostname for the FlashArray
            api_token: A valid api token for the FlashArray being managed

  2) From environment (PUREFA_IP and PUREFA_API)
  3) From the pillar (PUREFA_IP and PUREFA_API)

:maintainer: Simon Dodsley (simon@purestorage.com)
:maturity: new
:requires: purestorage
:platform: all

.. versionadded:: 2018.3.0

"""


import os
import platform
from datetime import datetime

from salt.exceptions import CommandExecutionError

# Import 3rd party modules
try:
    import purestorage

    HAS_PURESTORAGE = True
except ImportError:
    HAS_PURESTORAGE = False

__docformat__ = "restructuredtext en"

VERSION = "1.0.0"
USER_AGENT_BASE = "Salt"

__virtualname__ = "purefa"

# Default symbols to use for passwords. Avoids visually confusing characters.
# ~6 bits per symbol
DEFAULT_PASSWORD_SYMBOLS = (
    "23456789",  # Removed: 0,1
    "ABCDEFGHJKLMNPQRSTUVWXYZ",  # Removed: I, O
    "abcdefghijkmnopqrstuvwxyz",
)  # Removed: l


def __virtual__():
    """
    Determine whether or not to load this module
    """
    if HAS_PURESTORAGE:
        return __virtualname__
    return (
        False,
        "purefa execution module not loaded: purestorage python library not available.",
    )


def _get_system():
    """
    Get Pure Storage FlashArray configuration

    1) From the minion config
        pure_tags:
          fa:
            san_ip: management vip or hostname for the FlashArray
            api_token: A valid api token for the FlashArray being managed
    2) From environment (PUREFA_IP and PUREFA_API)
    3) From the pillar (PUREFA_IP and PUREFA_API)

    """
    agent = {
        "base": USER_AGENT_BASE,
        "class": __name__,
        "version": VERSION,
        "platform": platform.platform(),
    }

    user_agent = "{base} {class}/{version} ({platform})".format(**agent)

    try:
        array = __opts__["pure_tags"]["fa"].get("san_ip")
        api = __opts__["pure_tags"]["fa"].get("api_token")
        if array and api:
            system = purestorage.FlashArray(array, api_token=api, user_agent=user_agent)
    except (KeyError, NameError, TypeError):
        try:
            san_ip = os.environ.get("PUREFA_IP")
            api_token = os.environ.get("PUREFA_API")
            system = purestorage.FlashArray(
                san_ip, api_token=api_token, user_agent=user_agent
            )
        except (ValueError, KeyError, NameError):
            try:
                system = purestorage.FlashArray(
                    __pillar__["PUREFA_IP"],
                    api_token=__pillar__["PUREFA_API"],
                    user_agent=user_agent,
                )
            except (KeyError, NameError):
                raise CommandExecutionError(
                    "No Pure Storage FlashArray credentials found."
                )

    try:
        system.get()
    except Exception:  # pylint: disable=broad-except
        raise CommandExecutionError("Pure Storage FlashArray authentication failed.")
    return system


def _get_volume(name, array):
    """Private function to check volume"""
    try:
        return array.get_volume(name)
    except purestorage.PureError:
        return None


def _get_snapshot(name, suffix, array):
    """Private function to check snapshot"""
    snapshot = name + "." + suffix
    try:
        for snap in array.get_volume(name, snap=True):
            if snap["name"] == snapshot:
                return snapshot
    except purestorage.PureError:
        return None


def _get_deleted_volume(name, array):
    """Private function to check deleted volume"""
    try:
        return array.get_volume(name, pending="true")
    except purestorage.PureError:
        return None


def _get_pgroup(name, array):
    """Private function to check protection group"""
    pgroup = None
    for temp in array.list_pgroups():
        if temp["name"] == name:
            pgroup = temp
            break
    return pgroup


def _get_deleted_pgroup(name, array):
    """Private function to check deleted protection group"""
    try:
        return array.get_pgroup(name, pending="true")
    except purestorage.PureError:
        return None


def _get_hgroup(name, array):
    """Private function to check hostgroup"""
    hostgroup = None
    for temp in array.list_hgroups():
        if temp["name"] == name:
            hostgroup = temp
            break
    return hostgroup


def _get_host(name, array):
    """Private function to check host"""
    host = None
    for temp in array.list_hosts():
        if temp["name"] == name:
            host = temp
            break
    return host


def snap_create(name, suffix=None):
    """

    Create a volume snapshot on a Pure Storage FlashArray.

    Will return False is volume selected to snap does not exist.

    .. versionadded:: 2018.3.0

    name : string
        name of volume to snapshot
    suffix : string
        if specificed forces snapshot name suffix. If not specified defaults to timestamp.

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.snap_create foo
        salt '*' purefa.snap_create foo suffix=bar

    """
    array = _get_system()
    if suffix is None:
        suffix = "snap-" + str(
            (datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()
        )
        suffix = suffix.replace(".", "")
    if _get_volume(name, array) is not None:
        try:
            array.create_snapshot(name, suffix=suffix)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def snap_delete(name, suffix=None, eradicate=False):
    """

    Delete a volume snapshot on a Pure Storage FlashArray.

    Will return False if selected snapshot does not exist.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    suffix : string
        name of snapshot
    eradicate : boolean
        Eradicate snapshot after deletion if True. Default is False

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.snap_delete foo suffix=snap eradicate=True

    """
    array = _get_system()
    if _get_snapshot(name, suffix, array) is not None:
        try:
            snapname = name + "." + suffix
            array.destroy_volume(snapname)
        except purestorage.PureError:
            return False
        if eradicate is True:
            try:
                array.eradicate_volume(snapname)
                return True
            except purestorage.PureError:
                return False
        else:
            return True
    else:
        return False


def snap_eradicate(name, suffix=None):
    """

    Eradicate a deleted volume snapshot on a Pure Storage FlashArray.

    Will return False if snapshot is not in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    suffix : string
        name of snapshot

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.snap_eradicate foo suffix=snap

    """
    array = _get_system()
    if _get_snapshot(name, suffix, array) is not None:
        snapname = name + "." + suffix
        try:
            array.eradicate_volume(snapname)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def volume_create(name, size=None):
    """

    Create a volume on a Pure Storage FlashArray.

    Will return False if volume already exists.

    .. versionadded:: 2018.3.0

    name : string
        name of volume (truncated to 63 characters)
    size : string
        if specificed capacity of volume. If not specified default to 1G.
        Refer to Pure Storage documentation for formatting rules.

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_create foo
        salt '*' purefa.volume_create foo size=10T

    """
    if len(name) > 63:
        name = name[0:63]
    array = _get_system()
    if _get_volume(name, array) is None:
        if size is None:
            size = "1G"
        try:
            array.create_volume(name, size)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def volume_delete(name, eradicate=False):
    """

    Delete a volume on a Pure Storage FlashArray.

    Will return False if volume doesn't exist is already in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    eradicate : boolean
        Eradicate volume after deletion if True. Default is False

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_delete foo eradicate=True

    """
    array = _get_system()
    if _get_volume(name, array) is not None:
        try:
            array.destroy_volume(name)
        except purestorage.PureError:
            return False
        if eradicate is True:
            try:
                array.eradicate_volume(name)
                return True
            except purestorage.PureError:
                return False
        else:
            return True
    else:
        return False


def volume_eradicate(name):
    """

    Eradicate a deleted volume on a Pure Storage FlashArray.

    Will return False is volume is not in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of volume

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_eradicate foo

    """
    array = _get_system()
    if _get_deleted_volume(name, array) is not None:
        try:
            array.eradicate_volume(name)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def volume_extend(name, size):
    """

    Extend an existing volume on a Pure Storage FlashArray.

    Will return False if new size is less than or equal to existing size.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    size : string
        New capacity of volume.
        Refer to Pure Storage documentation for formatting rules.

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_extend foo 10T

    """
    array = _get_system()
    vol = _get_volume(name, array)
    if vol is not None:
        if __utils__["stringutils.human_to_bytes"](size) > vol["size"]:
            try:
                array.extend_volume(name, size)
                return True
            except purestorage.PureError:
                return False
        else:
            return False
    else:
        return False


def snap_volume_create(name, target, overwrite=False):
    """

    Create R/W volume from snapshot on a Pure Storage FlashArray.

    Will return False if target volume already exists and
    overwrite is not specified, or selected snapshot doesn't exist.

    .. versionadded:: 2018.3.0

    name : string
        name of volume snapshot
    target : string
        name of clone volume
    overwrite : boolean
        overwrite clone if already exists (default: False)

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.snap_volume_create foo.bar clone overwrite=True

    """
    array = _get_system()
    source, suffix = name.split(".")
    if _get_snapshot(source, suffix, array) is not None:
        if _get_volume(target, array) is None:
            try:
                array.copy_volume(name, target)
                return True
            except purestorage.PureError:
                return False
        else:
            if overwrite:
                try:
                    array.copy_volume(name, target, overwrite=overwrite)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
    else:
        return False


def volume_clone(name, target, overwrite=False):
    """

    Clone an existing volume on a Pure Storage FlashArray.

    Will return False if source volume doesn't exist, or
    target volume already exists and overwrite not specified.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    target : string
        name of clone volume
    overwrite : boolean
        overwrite clone if already exists (default: False)

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_clone foo bar overwrite=True

    """
    array = _get_system()
    if _get_volume(name, array) is not None:
        if _get_volume(target, array) is None:
            try:
                array.copy_volume(name, target)
                return True
            except purestorage.PureError:
                return False
        else:
            if overwrite:
                try:
                    array.copy_volume(name, target, overwrite=overwrite)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
    else:
        return False


def volume_attach(name, host):
    """

    Attach a volume to a host on a Pure Storage FlashArray.

    Host and volume must exist or else will return False.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    host : string
        name of host

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_attach foo bar

    """
    array = _get_system()
    if _get_volume(name, array) is not None and _get_host(host, array) is not None:
        try:
            array.connect_host(host, name)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def volume_detach(name, host):
    """

    Detach a volume from a host on a Pure Storage FlashArray.

    Will return False if either host or volume do not exist, or
    if selected volume isn't already connected to the host.

    .. versionadded:: 2018.3.0

    name : string
        name of volume
    host : string
        name of host

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.volume_detach foo bar

    """
    array = _get_system()
    if _get_volume(name, array) is None or _get_host(host, array) is None:
        return False
    elif _get_volume(name, array) is not None and _get_host(host, array) is not None:
        try:
            array.disconnect_host(host, name)
            return True
        except purestorage.PureError:
            return False


def host_create(name, iqn=None, wwn=None, nqn=None):
    """

    Add a host on a Pure Storage FlashArray.

    Will return False if host already exists, or the iSCSI or
    Fibre Channel parameters are not in a valid format.
    See Pure Storage FlashArray documentation.

    .. versionadded:: 2018.3.0

    name : string
        name of host (truncated to 63 characters)
    iqn : string
        iSCSI IQN of host
    nqn : string
        NVMeF NQN of host
        .. versionadded:: 3006.0
    wwn : string
        Fibre Channel WWN of host

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.host_create foo iqn='<Valid iSCSI IQN>' wwn='<Valid WWN>' nqn='<Valid NQN>'

    """
    array = _get_system()
    if len(name) > 63:
        name = name[0:63]
    if _get_host(name, array) is None:
        try:
            array.create_host(name)
        except purestorage.PureError:
            return False
        if nqn:
            try:
                array.set_host(name, addnqnlist=[nqn])
            except purestorage.PureError:
                array.delete_host(name)
                return False
        if iqn is not None:
            try:
                array.set_host(name, addiqnlist=[iqn])
            except purestorage.PureError:
                array.delete_host(name)
                return False
        if wwn is not None:
            try:
                array.set_host(name, addwwnlist=[wwn])
            except purestorage.PureError:
                array.delete_host(name)
                return False
    else:
        return False

    return True


def host_update(name, iqn=None, wwn=None, nqn=None):
    """

    Update a hosts port definitions on a Pure Storage FlashArray.

    Will return False if new port definitions are already in use
    by another host, or are not in a valid format.
    See Pure Storage FlashArray documentation.

    .. versionadded:: 2018.3.0

    name : string
        name of host
    nqn : string
        Additional NVMeF NQN of host
        .. versionadded:: 3006.0
    iqn : string
        Additional iSCSI IQN of host
    wwn : string
        Additional Fibre Channel WWN of host

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.host_update foo iqn='<Valid iSCSI IQN>' wwn='<Valid WWN>' nqn='<Valid NQN>'

    """
    array = _get_system()
    if _get_host(name, array) is not None:
        if nqn:
            try:
                array.set_host(name, addnqnlist=[nqn])
            except purestorage.PureError:
                return False
        if iqn is not None:
            try:
                array.set_host(name, addiqnlist=[iqn])
            except purestorage.PureError:
                return False
        if wwn is not None:
            try:
                array.set_host(name, addwwnlist=[wwn])
            except purestorage.PureError:
                return False
        return True
    else:
        return False


def host_delete(name):
    """

    Delete a host on a Pure Storage FlashArray (detaches all volumes).

    Will return False if the host doesn't exist.

    .. versionadded:: 2018.3.0

    name : string
        name of host

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.host_delete foo

    """
    array = _get_system()
    if _get_host(name, array) is not None:
        for vol in array.list_host_connections(name):
            try:
                array.disconnect_host(name, vol["vol"])
            except purestorage.PureError:
                return False
        try:
            array.delete_host(name)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def hg_create(name, host=None, volume=None):
    """

    Create a hostgroup on a Pure Storage FlashArray.

    Will return False if hostgroup already exists, or if
    named host or volume do not exist.

    .. versionadded:: 2018.3.0

    name : string
        name of hostgroup (truncated to 63 characters)
    host  : string
         name of host to add to hostgroup
    volume : string
         name of volume to add to hostgroup

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.hg_create foo host=bar volume=vol

    """
    array = _get_system()
    if len(name) > 63:
        name = name[0:63]
    if _get_hgroup(name, array) is None:
        try:
            array.create_hgroup(name)
        except purestorage.PureError:
            return False
        if host is not None:
            if _get_host(host, array):
                try:
                    array.set_hgroup(name, addhostlist=[host])
                except purestorage.PureError:
                    return False
            else:
                hg_delete(name)
                return False
        if volume is not None:
            if _get_volume(volume, array):
                try:
                    array.connect_hgroup(name, volume)
                except purestorage.PureError:
                    hg_delete(name)
                    return False
            else:
                hg_delete(name)
                return False
        return True
    else:
        return False


def hg_update(name, host=None, volume=None):
    """

    Adds entries to a hostgroup on a Pure Storage FlashArray.

    Will return False is hostgroup doesn't exist, or host
    or volume do not exist.

    .. versionadded:: 2018.3.0

    name : string
        name of hostgroup
    host  : string
         name of host to add to hostgroup
    volume : string
         name of volume to add to hostgroup

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.hg_update foo host=bar volume=vol

    """
    array = _get_system()
    if _get_hgroup(name, array) is not None:
        if host is not None:
            if _get_host(host, array):
                try:
                    array.set_hgroup(name, addhostlist=[host])
                except purestorage.PureError:
                    return False
            else:
                return False
        if volume is not None:
            if _get_volume(volume, array):
                try:
                    array.connect_hgroup(name, volume)
                except purestorage.PureError:
                    return False
            else:
                return False
        return True
    else:
        return False


def hg_delete(name):
    """

    Delete a hostgroup on a Pure Storage FlashArray (removes all volumes and hosts).

    Will return False is hostgroup is already in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of hostgroup

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.hg_delete foo

    """
    array = _get_system()
    if _get_hgroup(name, array) is not None:
        for vol in array.list_hgroup_connections(name):
            try:
                array.disconnect_hgroup(name, vol["vol"])
            except purestorage.PureError:
                return False
        host = array.get_hgroup(name)
        try:
            array.set_hgroup(name, remhostlist=host["hosts"])
            array.delete_hgroup(name)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def hg_remove(name, volume=None, host=None):
    """

    Remove a host and/or volume from a hostgroup on a Pure Storage FlashArray.

    Will return False is hostgroup does not exist, or named host or volume are
    not in the hostgroup.

    .. versionadded:: 2018.3.0

    name : string
        name of hostgroup
    volume : string
       name of volume to remove from hostgroup
    host : string
       name of host to remove from hostgroup

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.hg_remove foo volume=test host=bar

    """
    array = _get_system()
    if _get_hgroup(name, array) is not None:
        if volume is not None:
            if _get_volume(volume, array):
                for temp in array.list_hgroup_connections(name):
                    if temp["vol"] == volume:
                        try:
                            array.disconnect_hgroup(name, volume)
                            return True
                        except purestorage.PureError:
                            return False
                return False
            else:
                return False
        if host is not None:
            if _get_host(host, array):
                temp = _get_host(host, array)
                if temp["hgroup"] == name:
                    try:
                        array.set_hgroup(name, remhostlist=[host])
                        return True
                    except purestorage.PureError:
                        return False
                else:
                    return False
            else:
                return False
        if host is None and volume is None:
            return False
    else:
        return False


def pg_create(name, hostgroup=None, host=None, volume=None, enabled=True):
    """

    Create a protection group on a Pure Storage FlashArray.

    Will return False is the following cases:
       * Protection Grop already exists
       * Protection Group in a deleted state
       * More than one type is specified - protection groups are for only
         hostgroups, hosts or volumes
       * Named type for protection group does not exist

    .. versionadded:: 2018.3.0

    name : string
        name of protection group
    hostgroup  : string
         name of hostgroup to add to protection group
    host  : string
         name of host to add to protection group
    volume : string
         name of volume to add to protection group

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.pg_create foo [hostgroup=foo | host=bar | volume=vol] enabled=[true | false]

    """
    array = _get_system()
    if hostgroup is None and host is None and volume is None:
        if _get_pgroup(name, array) is None:
            try:
                array.create_pgroup(name)
            except purestorage.PureError:
                return False
            try:
                array.set_pgroup(name, snap_enabled=enabled)
                return True
            except purestorage.PureError:
                pg_delete(name)
                return False
        else:
            return False
    elif __utils__["value.xor"](hostgroup, host, volume):
        if _get_pgroup(name, array) is None:
            try:
                array.create_pgroup(name)
            except purestorage.PureError:
                return False
            try:
                array.set_pgroup(name, snap_enabled=enabled)
            except purestorage.PureError:
                pg_delete(name)
                return False
            if hostgroup is not None:
                if _get_hgroup(hostgroup, array) is not None:
                    try:
                        array.set_pgroup(name, addhgrouplist=[hostgroup])
                        return True
                    except purestorage.PureError:
                        pg_delete(name)
                        return False
                else:
                    pg_delete(name)
                    return False
            elif host is not None:
                if _get_host(host, array) is not None:
                    try:
                        array.set_pgroup(name, addhostlist=[host])
                        return True
                    except purestorage.PureError:
                        pg_delete(name)
                        return False
                else:
                    pg_delete(name)
                    return False
            elif volume is not None:
                if _get_volume(volume, array) is not None:
                    try:
                        array.set_pgroup(name, addvollist=[volume])
                        return True
                    except purestorage.PureError:
                        pg_delete(name)
                        return False
                else:
                    pg_delete(name)
                    return False
        else:
            return False
    else:
        return False


def pg_update(name, hostgroup=None, host=None, volume=None):
    """

    Update a protection group on a Pure Storage FlashArray.

    Will return False in the following cases:
      * Protection group does not exist
      * Incorrect type selected for current protection group type
      * Specified type does not exist

    .. versionadded:: 2018.3.0

    name : string
        name of protection group
    hostgroup  : string
         name of hostgroup to add to protection group
    host  : string
         name of host to add to protection group
    volume : string
         name of volume to add to protection group

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.pg_update foo [hostgroup=foo | host=bar | volume=vol]

    """
    array = _get_system()
    pgroup = _get_pgroup(name, array)
    if pgroup is not None:
        if hostgroup is not None and pgroup["hgroups"] is not None:
            if _get_hgroup(hostgroup, array) is not None:
                try:
                    array.add_hgroup(hostgroup, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        elif host is not None and pgroup["hosts"] is not None:
            if _get_host(host, array) is not None:
                try:
                    array.add_host(host, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        elif volume is not None and pgroup["volumes"] is not None:
            if _get_volume(volume, array) is not None:
                try:
                    array.add_volume(volume, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        else:
            if (
                pgroup["hgroups"] is None
                and pgroup["hosts"] is None
                and pgroup["volumes"] is None
            ):
                if hostgroup is not None:
                    if _get_hgroup(hostgroup, array) is not None:
                        try:
                            array.set_pgroup(name, addhgrouplist=[hostgroup])
                            return True
                        except purestorage.PureError:
                            return False
                    else:
                        return False
                elif host is not None:
                    if _get_host(host, array) is not None:
                        try:
                            array.set_pgroup(name, addhostlist=[host])
                            return True
                        except purestorage.PureError:
                            return False
                    else:
                        return False
                elif volume is not None:
                    if _get_volume(volume, array) is not None:
                        try:
                            array.set_pgroup(name, addvollist=[volume])
                            return True
                        except purestorage.PureError:
                            return False
                    else:
                        return False
            else:
                return False
    else:
        return False


def pg_delete(name, eradicate=False):
    """

    Delete a protecton group on a Pure Storage FlashArray.

    Will return False if protection group is already in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of protection group

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.pg_delete foo

    """
    array = _get_system()
    if _get_pgroup(name, array) is not None:
        try:
            array.destroy_pgroup(name)
        except purestorage.PureError:
            return False
        if eradicate is True:
            try:
                array.eradicate_pgroup(name)
                return True
            except purestorage.PureError:
                return False
        else:
            return True
    else:
        return False


def pg_eradicate(name):
    """

    Eradicate a deleted protecton group on a Pure Storage FlashArray.

    Will return False if protection group is not in a deleted state.

    .. versionadded:: 2018.3.0

    name : string
        name of protection group

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.pg_eradicate foo

    """
    array = _get_system()
    if _get_deleted_pgroup(name, array) is not None:
        try:
            array.eradicate_pgroup(name)
            return True
        except purestorage.PureError:
            return False
    else:
        return False


def pg_remove(name, hostgroup=None, host=None, volume=None):
    """

    Remove a hostgroup, host or volume from a protection group on a Pure Storage FlashArray.

    Will return False in the following cases:
      * Protection group does not exist
      * Specified type is not currently associated with the protection group

    .. versionadded:: 2018.3.0

    name : string
        name of hostgroup
    hostgroup  : string
         name of hostgroup to remove from protection group
    host  : string
         name of host to remove from hostgroup
    volume : string
         name of volume to remove from hostgroup

    CLI Example:

    .. code-block:: bash

        salt '*' purefa.pg_remove foo [hostgroup=bar | host=test | volume=bar]

    """
    array = _get_system()
    pgroup = _get_pgroup(name, array)
    if pgroup is not None:
        if hostgroup is not None and pgroup["hgroups"] is not None:
            if _get_hgroup(hostgroup, array) is not None:
                try:
                    array.remove_hgroup(hostgroup, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        elif host is not None and pgroup["hosts"] is not None:
            if _get_host(host, array) is not None:
                try:
                    array.remove_host(host, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        elif volume is not None and pgroup["volumes"] is not None:
            if _get_volume(volume, array) is not None:
                try:
                    array.remove_volume(volume, name)
                    return True
                except purestorage.PureError:
                    return False
            else:
                return False
        else:
            return False
    else:
        return False
