Ensure that the named interface is configured properly.

    Args:

        name (str):
            The name of the interface to manage

        dns_proto (str): None
            Set to ``static`` and use the ``dns_servers`` parameter to provide a
            list of DNS nameservers. set to ``dhcp`` to use DHCP to get the DNS
            servers.

        dns_servers (list): None
            A list of static DNS servers. To clear the list of DNS servers pass
            an empty list (``[]``). ``None`` will make no changes.

        ip_proto (str): None
            Set to ``static`` and use the ``ip_addrs`` and (optionally)
            ``gateway`` parameters to provide a list of static IP addresses and
            the default gateway. Set to ``dhcp`` to use DHCP.

        ip_addrs (list): None
            A list of static IP addresses with netmask flag, ie: 192.168.0.11/24

        gateway (str): None
            The gateway to set for the interface

        enabled (bool): True
            Set to ``False`` to ensure that this interface is disabled.

    Returns:
        dict: A dictionary of old and new settings

    Example:

    .. code-block:: yaml

        Ethernet1:
          network.managed:
            - dns_proto: static
            - dns_servers:
              - 8.8.8.8
              - 8.8.8.4
            - ip_proto: static
            - ip_addrs:
              - 192.168.0.100/24

    Clear DNS entries example:

    .. code-block:: yaml

        Ethernet1:
          network.managed:
            - dns_proto: static
            - dns_servers: []
            - ip_proto: dhcp

def managed(name,
            dns_proto=None,
            dns_servers=None,
            ip_proto=None,
            ip_addrs=None,
            gateway=None,
            enabled=True,
            **kwargs):
    '''
    Ensure that the named interface is configured properly.

    Args:

        name (str):
            The name of the interface to manage

        dns_proto (str): None
            Set to ``static`` and use the ``dns_servers`` parameter to provide a
            list of DNS nameservers. set to ``dhcp`` to use DHCP to get the DNS
            servers.

        dns_servers (list): None
            A list of static DNS servers. To clear the list of DNS servers pass
            an empty list (``[]``). ``None`` will make no changes.

        ip_proto (str): None
            Set to ``static`` and use the ``ip_addrs`` and (optionally)
            ``gateway`` parameters to provide a list of static IP addresses and
            the default gateway. Set to ``dhcp`` to use DHCP.

        ip_addrs (list): None
            A list of static IP addresses with netmask flag, ie: 192.168.0.11/24

        gateway (str): None
            The gateway to set for the interface

        enabled (bool): True
            Set to ``False`` to ensure that this interface is disabled.

    Returns:
        dict: A dictionary of old and new settings

    Example:

    .. code-block:: yaml

        Ethernet1:
          network.managed:
            - dns_proto: static
            - dns_servers:
              - 8.8.8.8
              - 8.8.8.4
            - ip_proto: static
            - ip_addrs:
              - 192.168.0.100/24

    Clear DNS entries example:

    .. code-block:: yaml

        Ethernet1:
          network.managed:
            - dns_proto: static
            - dns_servers: []
            - ip_proto: dhcp
    '''
    ret = {
        'name': name,
        'changes': {},
        'result': True,
        'comment': 'Interface \'{0}\' is up to date'.format(name)
    }

    dns_proto = six.text_type(dns_proto).lower()
    ip_proto = six.text_type(ip_proto).lower()

    errors = []
    if dns_proto not in __VALID_PROTO:
        ret['result'] = False
        errors.append('dns_proto must be one of the following: {0}'
                      .format(', '.join(__VALID_PROTO)))

    if ip_proto not in __VALID_PROTO:
        errors.append('ip_proto must be one of the following: {0}'
                      .format(', '.join(__VALID_PROTO)))

    if errors:
        ret['result'] = False
        ret['comment'] = '\n'.join(errors)
        return ret

    try:
        currently_enabled = __salt__['ip.is_enabled'](name)
    except CommandExecutionError:
        currently_enabled = False

    if not enabled:
        if currently_enabled:
            if __opts__['test']:
                ret['result'] = None
                ret['comment'] = ('Interface \'{0}\' will be disabled'
                                  .format(name))
            else:
                ret['result'] = __salt__['ip.disable'](name)
                if not ret['result']:
                    ret['comment'] = ('Failed to disable interface \'{0}\''
                                      .format(name))
        else:
            ret['comment'] += ' (already disabled)'
        return ret
    else:
        if not currently_enabled:
            if __opts__['test']:
                ret['result'] = None
                ret['comment'] = ('Interface \'{0}\' will be enabled'
                                  .format(name))
            else:
                if not __salt__['ip.enable'](name):
                    ret['result'] = False
                    ret['comment'] = ('Failed to enable interface \'{0}\' to '
                                      'make changes'.format(name))
                    return ret

        errors = _validate(dns_proto, dns_servers, ip_proto, ip_addrs, gateway)
        if errors:
            ret['result'] = False
            ret['comment'] = ('The following SLS configuration errors were '
                              'detected:\n- {0}'.format('\n- '.join(errors)))
            return ret

        old = __salt__['ip.get_interface'](name)
        if not old:
            ret['result'] = False
            ret['comment'] = ('Unable to get current configuration for '
                              'interface \'{0}\''.format(name))
            return ret

        changes = _changes(old,
                           dns_proto,
                           dns_servers,
                           ip_proto,
                           ip_addrs,
                           gateway)

        # If dns_servers is the default `None` make no changes
        # To clear the list, pass an empty dict
        if str(dns_servers).lower() == 'none':
            changes.pop('dns_servers', None)

        if not changes:
            return ret

        if __opts__['test']:
            comments = []
            if 'dns_proto' in changes:
                comments.append('DNS protocol will be changed to: {0}'
                                .format(changes['dns_proto']))
            if dns_proto == 'static' and 'dns_servers' in changes:
                if not changes['dns_servers']:
                    comments.append('The list of DNS servers will be cleared')
                else:
                    comments.append(
                        'DNS servers will be set to the following: {0}'
                        .format(', '.join(changes['dns_servers']))
                    )
            if 'ip_proto' in changes:
                comments.append('IP protocol will be changed to: {0}'
                                .format(changes['ip_proto']))
            if ip_proto == 'static':
                if 'ip_addrs' in changes:
                    comments.append(
                        'IP addresses will be set to the following: {0}'
                        .format(', '.join(changes['ip_addrs']))
                    )
                if 'gateway' in changes:
                    if changes['gateway'] is None:
                        comments.append('Default gateway will be removed')
                    else:
                        comments.append(
                            'Default gateway will be set to {0}'
                            .format(changes['gateway'])
                        )

            ret['result'] = None
            ret['comment'] = ('The following changes will be made to '
                              'interface \'{0}\':\n- {1}'
                              .format(name, '\n- '.join(comments)))
            return ret

        if changes.get('dns_proto') == 'dhcp':
            __salt__['ip.set_dhcp_dns'](name)

        elif 'dns_servers' in changes:
            if not changes['dns_servers']:
                # To clear the list of DNS servers you have to pass []. Later
                # changes gets passed like *args and a single empty list is
                # converted to an empty tuple. So, you have to add [] here
                changes['dns_servers'] = [[]]

            __salt__['ip.set_static_dns'](name, *changes['dns_servers'])

        if changes.get('ip_proto') == 'dhcp':
            __salt__['ip.set_dhcp_ip'](name)
        elif changes.get('ip_addrs') or changes.get('gateway') or changes.get('ip_proto') == 'static':
            if changes.get('gateway') and not changes.get('ip_addrs'):
                changes['ip_addrs'] = ip_addrs
            if changes.get('ip_proto') == 'static' and not changes.get('ip_addrs'):
                changes['ip_addrs'] = ip_addrs
            for idx in range(len(changes['ip_addrs'])):
                if idx == 0:
                    __salt__['ip.set_static_ip'](
                        name,
                        changes['ip_addrs'][idx],
                        gateway=gateway,
                        append=False
                    )
                else:
                    __salt__['ip.set_static_ip'](
                        name,
                        changes['ip_addrs'][idx],
                        gateway=None,
                        append=True
                    )

        new = __salt__['ip.get_interface'](name)
        ret['changes'] = salt.utils.data.compare_dicts(old, new)
        if _changes(new, dns_proto, dns_servers, ip_proto, ip_addrs, gateway):
            ret['result'] = False
            ret['comment'] = ('Failed to set desired configuration settings '
                              'for interface \'{0}\''.format(name))
        else:
            ret['comment'] = ('Successfully updated configuration for '
                              'interface \'{0}\''.format(name))
        return ret