Set static IP configuration on a Windows NIC

    iface
        The name of the interface to manage

    addr
        IP address with subnet length (ex. ``10.1.2.3/24``). The
        :mod:`ip.get_subnet_length <salt.modules.win_ip.get_subnet_length>`
        function can be used to calculate the subnet length from a netmask.

    gateway : None
        If specified, the default gateway will be set to this value.

    append : False
        If ``True``, this IP address will be added to the interface. Default is
        ``False``, which overrides any existing configuration for the interface
        and sets ``addr`` as the only address on the interface.

    CLI Example:

    .. code-block:: bash

        salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.3/24 gateway=10.1.2.1
        salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.4/24 append=True

def set_static_ip(iface, addr, gateway=None, append=False):
    '''
    Set static IP configuration on a Windows NIC

    iface
        The name of the interface to manage

    addr
        IP address with subnet length (ex. ``10.1.2.3/24``). The
        :mod:`ip.get_subnet_length <salt.modules.win_ip.get_subnet_length>`
        function can be used to calculate the subnet length from a netmask.

    gateway : None
        If specified, the default gateway will be set to this value.

    append : False
        If ``True``, this IP address will be added to the interface. Default is
        ``False``, which overrides any existing configuration for the interface
        and sets ``addr`` as the only address on the interface.

    CLI Example:

    .. code-block:: bash

        salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.3/24 gateway=10.1.2.1
        salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.4/24 append=True
    '''
    def _find_addr(iface, addr, timeout=1):
        ip, cidr = addr.rsplit('/', 1)
        netmask = salt.utils.network.cidr_to_ipv4_netmask(cidr)
        for idx in range(timeout):
            for addrinfo in get_interface(iface).get('ip_addrs', []):
                if addrinfo['IP Address'] == ip \
                        and addrinfo['Netmask'] == netmask:
                    return addrinfo
            time.sleep(1)
        return {}

    if not salt.utils.validate.net.ipv4_addr(addr):
        raise SaltInvocationError('Invalid address \'{0}\''.format(addr))

    if gateway and not salt.utils.validate.net.ipv4_addr(addr):
        raise SaltInvocationError(
            'Invalid default gateway \'{0}\''.format(gateway)
        )

    if '/' not in addr:
        addr += '/32'

    if append and _find_addr(iface, addr):
        raise CommandExecutionError(
            'Address \'{0}\' already exists on interface '
            '\'{1}\''.format(addr, iface)
        )

    cmd = ['netsh', 'interface', 'ip']
    if append:
        cmd.append('add')
    else:
        cmd.append('set')
    cmd.extend(['address', 'name={0}'.format(iface)])
    if not append:
        cmd.append('source=static')
    cmd.append('address={0}'.format(addr))
    if gateway:
        cmd.append('gateway={0}'.format(gateway))

    result = __salt__['cmd.run_all'](cmd, python_shell=False)
    if result['retcode'] != 0:
        raise CommandExecutionError(
            'Unable to set IP address: {0}'.format(result['stderr'])
        )

    new_addr = _find_addr(iface, addr, timeout=10)
    if not new_addr:
        return {}

    ret = {'Address Info': new_addr}
    if gateway:
        ret['Default Gateway'] = gateway
    return ret