# Buggy code
def parse_ip_output(output, secondary=False):
    for line in output.split("\n"):
        if not line:
            continue
        words = line.split()
        broadcast = ""
        if words[0] == "inet":
            if "/" in words[1]:
                address, netmask_length = words[1].split("/")
                if len(words) > 3:
                    broadcast = words[3]
            else:
                # pointopoint interfaces do not have a prefix
                address = words[1]
                netmask_length = "32"
            address_bin = struct.unpack("!L", socket.inet_aton(address))[0]
            netmask_bin = (1 << 32) - (1 << 32 >> int(netmask_length))
            netmask = socket.inet_ntoa(struct.pack("!L", netmask_bin))
            network = socket.inet_ntoa(struct.pack("!L", address_bin & netmask_bin))
            iface = words[-1]
            if iface != device:
                interfaces[iface] = {}
            if not secondary and "ipv4" not in interfaces[iface]:
                interfaces[iface]["ipv4"] = {
                    "address": address,
                    "broadcast": broadcast,
                    "netmask": netmask,
                    "network": network,
                }
            else:
                if "ipv4_secondaries" not in interfaces[iface]:
                    interfaces[iface]["ipv4_secondaries"] = []
                interfaces[iface]["ipv4_secondaries"].append(
                    {
                        "address": address,
                        "broadcast": broadcast,
                        "netmask": netmask,
                        "network": network,
                    }
                )

            # add this secondary IP to the main device
            if secondary:
                if "ipv4_secondaries" not in interfaces[device]:
                    interfaces[device]["ipv4_secondaries"] = []
                interfaces[device]["ipv4_secondaries"].append(
                    {
                        "address": address,
                        "broadcast": broadcast,
                        "netmask": netmask,
                        "network": network,
                    }
                )

            # If this is the default address, update default_ipv4
            if "address" in default_ipv4 and default_ipv4["address"] == address:
                default_ipv4["broadcast"] = broadcast
                default_ipv4["netmask"] = netmask
                default_ipv4["network"] = network
                default_ipv4["macaddress"] = macaddress
                default_ipv4["mtu"] = interfaces[device]["mtu"]
                default_ipv4["type"] = interfaces[device].get("type", "unknown")
                default_ipv4["alias"] = words[-1]
            if not address.startswith("127."):
                ips["all_ipv4_addresses"].append(address)
        elif words[0] == "inet6":
            address, prefix = words[1].split("/")
            scope = words[3]
            if "ipv6" not in interfaces[device]:
                interfaces[device]["ipv6"] = []
            interfaces[device]["ipv6"].append(
                {"address": address, "prefix": prefix, "scope": scope}
            )
            # If this is the default address, update default_ipv6
            if "address" in default_ipv6 and default_ipv6["address"] == address:
                default_ipv6["prefix"] = prefix
                default_ipv6["scope"] = scope
                default_ipv6["macaddress"] = macaddress
                default_ipv6["mtu"] = interfaces[device]["mtu"]
                default_ipv6["type"] = interfaces[device].get("type", "unknown")
            if not address == "::1":
                ips["all_ipv6_addresses"].append(address)


# Traceback (ValueError)
» ansible -vvv castor.home.universe.tf -m setup
Using /home/dave/hack/ansible/ansible.cfg as config file
<castor.home.universe.tf> ESTABLISH SSH CONNECTION FOR USER: root
<castor.home.universe.tf> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/home/dave/.ansible/cp/ansible-ssh-%h-%p-%r castor.home.universe.tf '/bin/sh -c '"'"'LANG=en_US.utf8 LC_ALL=en_US.utf8 LC_MESSAGES=en_US.utf8 /usr/bin/python'"'"''
An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
File "<stdin>", line 5184, in <module>
File "<stdin>", line 140, in main
File "<stdin>", line 84, in run_setup
File "<stdin>", line 5121, in ansible_facts
File "<stdin>", line 4015, in populate
File "<stdin>", line 4211, in get_interfaces_info
File "<stdin>", line 4182, in parse_ip_output
ValueError: need more than 1 value to unpack

castor.home.universe.tf | FAILED! => {
"changed": false,
"failed": true,
"invocation": {
"module_name": "setup"
},
"parsed": false
}

# Fixed code
def parse_ip_output(output, secondary=False):
    for line in output.split("\n"):
        if not line:
            continue
        words = line.split()
        broadcast = ""
        if words[0] == "inet":
            if "/" in words[1]:
                address, netmask_length = words[1].split("/")
                if len(words) > 3:
                    broadcast = words[3]
            else:
                # pointopoint interfaces do not have a prefix
                address = words[1]
                netmask_length = "32"
            address_bin = struct.unpack("!L", socket.inet_aton(address))[0]
            netmask_bin = (1 << 32) - (1 << 32 >> int(netmask_length))
            netmask = socket.inet_ntoa(struct.pack("!L", netmask_bin))
            network = socket.inet_ntoa(struct.pack("!L", address_bin & netmask_bin))
            iface = words[-1]
            if iface != device:
                interfaces[iface] = {}
            if not secondary and "ipv4" not in interfaces[iface]:
                interfaces[iface]["ipv4"] = {
                    "address": address,
                    "broadcast": broadcast,
                    "netmask": netmask,
                    "network": network,
                }
            else:
                if "ipv4_secondaries" not in interfaces[iface]:
                    interfaces[iface]["ipv4_secondaries"] = []
                interfaces[iface]["ipv4_secondaries"].append(
                    {
                        "address": address,
                        "broadcast": broadcast,
                        "netmask": netmask,
                        "network": network,
                    }
                )

            # add this secondary IP to the main device
            if secondary:
                if "ipv4_secondaries" not in interfaces[device]:
                    interfaces[device]["ipv4_secondaries"] = []
                interfaces[device]["ipv4_secondaries"].append(
                    {
                        "address": address,
                        "broadcast": broadcast,
                        "netmask": netmask,
                        "network": network,
                    }
                )

            # If this is the default address, update default_ipv4
            if "address" in default_ipv4 and default_ipv4["address"] == address:
                default_ipv4["broadcast"] = broadcast
                default_ipv4["netmask"] = netmask
                default_ipv4["network"] = network
                default_ipv4["macaddress"] = macaddress
                default_ipv4["mtu"] = interfaces[device]["mtu"]
                default_ipv4["type"] = interfaces[device].get("type", "unknown")
                default_ipv4["alias"] = words[-1]
            if not address.startswith("127."):
                ips["all_ipv4_addresses"].append(address)
        elif words[0] == "inet6":
            if "peer" == words[2]:
                address = words[1]
                _, prefix = words[3].split("/")
                scope = words[5]
            else:
                address, prefix = words[1].split("/")
                scope = words[3]
            if "ipv6" not in interfaces[device]:
                interfaces[device]["ipv6"] = []
            interfaces[device]["ipv6"].append(
                {"address": address, "prefix": prefix, "scope": scope}
            )
            # If this is the default address, update default_ipv6
            if "address" in default_ipv6 and default_ipv6["address"] == address:
                default_ipv6["prefix"] = prefix
                default_ipv6["scope"] = scope
                default_ipv6["macaddress"] = macaddress
                default_ipv6["mtu"] = interfaces[device]["mtu"]
                default_ipv6["type"] = interfaces[device].get("type", "unknown")
            if not address == "::1":
                ips["all_ipv6_addresses"].append(address)