Ensure a zone with certain properties and resources

    name : string
        name of the zone
    brand : string
        brand of the zone
    zonepath : string
        path of the zone
    properties : list of key-value pairs
        dict of properties
    resources : list of key-value pairs
        dict of resources

    .. note::
        If the zone does not exist it will not be installed.
        You can use the ```zone.installed``` state for this.

    .. note::
        Default resource selectors:
            - fs: dir
            - net: mac-addr
            - device: match
            - rctl: name
            - attr: name
            - dataset: name
            - admin: user

    .. warning::
        Properties and resource will not be removed when they
        are absent from the state!

        For properties, simple set them to ```None```.

        For resources, add the ```resource_prune``` property
        and set it to ```True```. Also specify the
        ```resource_selector_property``` if the default is not
        the one you want.

def present(name, brand, zonepath, properties=None, resources=None):
    '''
    Ensure a zone with certain properties and resources

    name : string
        name of the zone
    brand : string
        brand of the zone
    zonepath : string
        path of the zone
    properties : list of key-value pairs
        dict of properties
    resources : list of key-value pairs
        dict of resources

    .. note::
        If the zone does not exist it will not be installed.
        You can use the ```zone.installed``` state for this.

    .. note::
        Default resource selectors:
            - fs: dir
            - net: mac-addr
            - device: match
            - rctl: name
            - attr: name
            - dataset: name
            - admin: user

    .. warning::
        Properties and resource will not be removed when they
        are absent from the state!

        For properties, simple set them to ```None```.

        For resources, add the ```resource_prune``` property
        and set it to ```True```. Also specify the
        ```resource_selector_property``` if the default is not
        the one you want.

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

    ## sanitize defaults
    if not properties:
        properties = []
    if not resources:
        resources = []
    properties.append(OrderedDict({"brand": brand}))
    properties.append(OrderedDict({"zonepath": zonepath}))

    zones = __salt__['zoneadm.list'](installed=True, configured=True)

    ## test mode only has limited support
    if __opts__['test']:
        ret['result'] = None
        ret['comment'].append('Cannot determine of changes would happen to the zone {0}.'.format(name))

    ## create zone if needed
    if name not in zones:
        if __opts__['test']:
            ## we pretend we created the zone
            res_create = {'status': True}
            ret['comment'] = []
        else:
            ## create and install
            res_create = __salt__['zonecfg.create'](name, brand, zonepath)
        if res_create['status']:
            ret['result'] = True
            ret['changes'][name] = 'created'
            ret['comment'].append('The zone {0} was created.'.format(name))

    if not __opts__['test']:
        ret['result'] = True
        if isinstance(properties, list):
            for prop in properties:
                if not isinstance(prop, OrderedDict) or len(prop) != 1:
                    log.warning('zone.present - failed to parse property: %s', prop)
                    continue
                for key, value in prop.items():
                    res = None
                    if not value:
                        res = property_absent(name, key)
                    elif value:
                        res = property_present(name, key, value)
                    if res:
                        ret['result'] = ret['result'] if res['result'] else False
                        ret['comment'].append(res['comment'])
                        if res['changes']:
                            if 'property' not in ret['changes']:
                                ret['changes']['property'] = {}
                            ret['changes']['property'] = merge_dict(ret['changes']['property'], res['changes'])
        if isinstance(resources, list):
            for resource in resources:
                if not isinstance(prop, OrderedDict) or len(prop) != 1:
                    log.warning('zone.present - failed to parse resource: %s', resource)
                    continue
                for key, value in resource.items():
                    zonecfg = __salt__['zonecfg.info'](name, show_all=True)
                    resource_cfg = {}
                    resource_cfg['resource_type'] = key
                    if isinstance(value, list):
                        for respv in value:
                            resource_cfg.update(dict(respv))

                    resource_prune = False
                    resource_selector_property = None
                    if 'resource_prune' in resource_cfg:
                        resource_prune = resource_cfg['resource_prune']
                        del resource_cfg['resource_prune']
                    if 'resource_selector_property' in resource_cfg:
                        resource_selector_property = resource_cfg['resource_selector_property']
                        del resource_cfg['resource_selector_property']
                    if not resource_selector_property and key in _zonecfg_resource_default_selectors:
                        resource_selector_property = _zonecfg_resource_default_selectors[key]

                    res = None
                    if resource_prune:
                        res = resource_absent(
                            name,
                            resource_cfg['resource_type'],
                            resource_selector_property=resource_selector_property,
                            resource_selector_value=resource_cfg[resource_selector_property] if resource_selector_property else None,
                        )
                    else:
                        resource_cfg['resource_selector_property'] = resource_selector_property
                        if resource_selector_property in resource_cfg:
                            resource_cfg['resource_selector_value'] = resource_cfg[resource_selector_property]
                        else:
                            resource_cfg['resource_selector_value'] = None
                        resource_cfg['name'] = name  # we do this last because name can also be a attrib value
                        res = resource_present(**resource_cfg)
                    if res:
                        ret['result'] = ret['result'] if res['result'] else False
                        ret['comment'].append(res['comment'])
                        if res['changes']:
                            if 'resource' not in ret['changes']:
                                ret['changes']['resource'] = {}
                            ret['changes']['resource'] = merge_dict(ret['changes']['resource'], res['changes'])

    if isinstance(ret['comment'], list):
        ret['comment'] = "\n".join(ret['comment'])

    return ret