ensure storage pool is present on the system

    name : string
        name of storage pool
    properties : dict
        optional set of properties to set for the storage pool
    filesystem_properties : dict
        optional set of filesystem properties to set for the storage pool (creation only)
    layout: dict
        disk layout to use if the pool does not exist (creation only)
    config : dict
        fine grain control over this state

    .. note::

        The following configuration properties can be toggled in the config parameter.
          - import (true) - try to import the pool before creating it if absent
          - import_dirs (None) - specify additional locations to scan for devices on import (comma-seperated)
          - device_dir (None, SunOS=/dev/dsk, Linux=/dev) - specify device directory to prepend for none
            absolute device paths
          - force (false) - try to force the import or creation

    .. note::

        It is no longer needed to give a unique name to each top-level vdev, the old
        layout format is still supported but no longer recommended.

        .. code-block:: yaml

            - mirror:
              - /tmp/vdisk3
              - /tmp/vdisk2
            - mirror:
              - /tmp/vdisk0
              - /tmp/vdisk1

        The above yaml will always result in the following zpool create:

        .. code-block:: bash

            zpool create mypool mirror /tmp/vdisk3 /tmp/vdisk2 mirror /tmp/vdisk0 /tmp/vdisk1

    .. warning::

        The legacy format is also still supported but not recommended,
        because ID's inside the layout dict must be unique they need to have a suffix.

        .. code-block:: yaml

            mirror-0:
              /tmp/vdisk3
              /tmp/vdisk2
            mirror-1:
              /tmp/vdisk0
              /tmp/vdisk1

    .. warning::

        Pay attention to the order of your dict!

        .. code-block:: yaml

            - mirror:
              - /tmp/vdisk0
              - /tmp/vdisk1
            - /tmp/vdisk2

        The above will result in the following zpool create:

        .. code-block:: bash

            zpool create mypool mirror /tmp/vdisk0 /tmp/vdisk1 /tmp/vdisk2

        Creating a 3-way mirror! While you probably expect it to be mirror
        root vdev with 2 devices + a root vdev of 1 device!

def present(name, properties=None, filesystem_properties=None, layout=None, config=None):
    '''
    ensure storage pool is present on the system

    name : string
        name of storage pool
    properties : dict
        optional set of properties to set for the storage pool
    filesystem_properties : dict
        optional set of filesystem properties to set for the storage pool (creation only)
    layout: dict
        disk layout to use if the pool does not exist (creation only)
    config : dict
        fine grain control over this state

    .. note::

        The following configuration properties can be toggled in the config parameter.
          - import (true) - try to import the pool before creating it if absent
          - import_dirs (None) - specify additional locations to scan for devices on import (comma-seperated)
          - device_dir (None, SunOS=/dev/dsk, Linux=/dev) - specify device directory to prepend for none
            absolute device paths
          - force (false) - try to force the import or creation

    .. note::

        It is no longer needed to give a unique name to each top-level vdev, the old
        layout format is still supported but no longer recommended.

        .. code-block:: yaml

            - mirror:
              - /tmp/vdisk3
              - /tmp/vdisk2
            - mirror:
              - /tmp/vdisk0
              - /tmp/vdisk1

        The above yaml will always result in the following zpool create:

        .. code-block:: bash

            zpool create mypool mirror /tmp/vdisk3 /tmp/vdisk2 mirror /tmp/vdisk0 /tmp/vdisk1

    .. warning::

        The legacy format is also still supported but not recommended,
        because ID's inside the layout dict must be unique they need to have a suffix.

        .. code-block:: yaml

            mirror-0:
              /tmp/vdisk3
              /tmp/vdisk2
            mirror-1:
              /tmp/vdisk0
              /tmp/vdisk1

    .. warning::

        Pay attention to the order of your dict!

        .. code-block:: yaml

            - mirror:
              - /tmp/vdisk0
              - /tmp/vdisk1
            - /tmp/vdisk2

        The above will result in the following zpool create:

        .. code-block:: bash

            zpool create mypool mirror /tmp/vdisk0 /tmp/vdisk1 /tmp/vdisk2

        Creating a 3-way mirror! While you probably expect it to be mirror
        root vdev with 2 devices + a root vdev of 1 device!

    '''
    ret = {'name': name,
           'changes': {},
           'result': None,
           'comment': ''}

    # config defaults
    default_config = {
        'import': True,
        'import_dirs': None,
        'device_dir': None,
        'force': False
    }
    if __grains__['kernel'] == 'SunOS':
        default_config['device_dir'] = '/dev/dsk'
    elif __grains__['kernel'] == 'Linux':
        default_config['device_dir'] = '/dev'

    # merge state config
    if config:
        default_config.update(config)
    config = default_config

    # ensure properties are zfs values
    if properties:
        properties = __utils__['zfs.from_auto_dict'](properties)
    elif properties is None:
        properties = {}
    if filesystem_properties:
        filesystem_properties = __utils__['zfs.from_auto_dict'](filesystem_properties)
    elif filesystem_properties is None:
        filesystem_properties = {}

    # parse layout
    vdevs = _layout_to_vdev(layout, config['device_dir'])
    if vdevs:
        vdevs.insert(0, name)

    # log configuration
    log.debug('zpool.present::%s::config - %s', name, config)
    log.debug('zpool.present::%s::vdevs - %s', name, vdevs)
    log.debug('zpool.present::%s::properties -  %s', name, properties)
    log.debug('zpool.present::%s::filesystem_properties -  %s', name, filesystem_properties)

    # ensure the pool is present
    ret['result'] = False

    # don't do anything because this is a test
    if __opts__['test']:
        ret['result'] = True
        if __salt__['zpool.exists'](name):
            ret['changes'][name] = 'uptodate'
        else:
            ret['changes'][name] = 'imported' if config['import'] else 'created'
        ret['comment'] = 'storage pool {0} was {1}'.format(name, ret['changes'][name])

    # update pool
    elif __salt__['zpool.exists'](name):
        ret['result'] = True

        # fetch current pool properties
        properties_current = __salt__['zpool.get'](name, parsable=True)

        # build list of properties to update
        properties_update = []
        if properties:
            for prop in properties:
                # skip unexisting properties
                if prop not in properties_current:
                    log.warning('zpool.present::%s::update - unknown property: %s', name, prop)
                    continue

                # compare current and wanted value
                if properties_current[prop] != properties[prop]:
                    properties_update.append(prop)

        # update pool properties
        for prop in properties_update:
            res = __salt__['zpool.set'](name, prop, properties[prop])

            if res['set']:
                if name not in ret['changes']:
                    ret['changes'][name] = {}
                ret['changes'][name][prop] = properties[prop]
            else:
                ret['result'] = False
                if ret['comment'] == '':
                    ret['comment'] = 'The following properties were not updated:'
                ret['comment'] = '{0} {1}'.format(ret['comment'], prop)

        if ret['result']:
            ret['comment'] = 'properties updated' if ret['changes'] else 'no update needed'

    # import or create the pool (at least try to anyway)
    else:
        # import pool
        if config['import']:
            mod_res = __salt__['zpool.import'](
                name,
                force=config['force'],
                dir=config['import_dirs'],
            )

            ret['result'] = mod_res['imported']
            if ret['result']:
                ret['changes'][name] = 'imported'
                ret['comment'] = 'storage pool {0} was imported'.format(name)

        # create pool
        if not ret['result'] and vdevs:
            log.debug('zpool.present::%s::creating', name)

            # execute zpool.create
            mod_res = __salt__['zpool.create'](
                *vdevs,
                force=config['force'],
                properties=properties,
                filesystem_properties=filesystem_properties
            )

            ret['result'] = mod_res['created']
            if ret['result']:
                ret['changes'][name] = 'created'
                ret['comment'] = 'storage pool {0} was created'.format(name)
            elif 'error' in mod_res:
                ret['comment'] = mod_res['error']
            else:
                ret['comment'] = 'could not create storage pool {0}'.format(name)

        # give up, we cannot import the pool and we do not have a layout to create it
        if not ret['result'] and not vdevs:
            ret['comment'] = 'storage pool {0} was not imported, no (valid) layout specified for creation'.format(name)

    return ret