saltstack/salt

View on GitHub
salt/modules/purefa.py

Summary

Maintainability
F
2 wks
Test Coverage
# -*- coding: utf-8 -*-

##
# 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 Python libs
from __future__ import absolute_import, print_function, unicode_literals
import os
import platform
from datetime import datetime

# Import Salt libs
from salt.ext import six
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:
        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-' + six.text_type((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
    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 is not None:
            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
    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 is not None:
            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