Example #1
0
    def test_ignores_whitespace_lines(self):
        testdata = dedent(
            """

        1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN \
mode DEFAULT group default


            link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

            inet 127.0.0.1/8 scope host lo
                valid_lft forever preferred_lft forever

            inet6 ::1/128 scope host
                valid_lft forever preferred_lft forever

        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000

            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff

        """
        )
        ip_link = parse_ip_addr(testdata)
        # Sanity check to ensure some data exists
        self.assertIsNotNone(ip_link.get("lo"))
        self.assertIsNotNone(ip_link.get("eth0"))
        self.assertIsNotNone(ip_link["eth0"].get("mac"))
Example #2
0
    def test_parses_xenial_interfaces(self):
        testdata = dedent(
            """
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN \
group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP \
group default qlen 1000
    link/ether 52:54:00:2d:39:49 brd ff:ff:ff:ff:ff:ff
    inet 172.16.100.108/24 brd 172.16.100.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe2d:3949/64 scope link
       valid_lft forever preferred_lft forever
3: ens10: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN \
group default qlen 1000
    link/ether 52:54:00:e5:c6:6b brd ff:ff:ff:ff:ff:ff
4: ens11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN \
group default qlen 1000
    link/ether 52:54:00:ed:9f:9d brd ff:ff:ff:ff:ff:ff
       """
        )
        ip_link = parse_ip_addr(testdata)
        self.assertEqual(2, ip_link["ens3"]["index"])
        self.assertEqual("52:54:00:2d:39:49", ip_link["ens3"]["mac"])
        self.assertEqual(["172.16.100.108/24"], ip_link["ens3"]["inet"])
        self.assertEqual(3, ip_link["ens10"]["index"])
        self.assertEqual("52:54:00:e5:c6:6b", ip_link["ens10"]["mac"])
        self.assertThat(ip_link["ens10"], Not(Contains("inet")))
        self.assertEqual(4, ip_link["ens11"]["index"])
        self.assertEqual("52:54:00:ed:9f:9d", ip_link["ens11"]["mac"])
        self.assertThat(ip_link["ens11"], Not(Contains("inet")))
Example #3
0
    def test_parses_mac(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
        """)
        ip_link = parse_ip_addr(testdata)
        self.assertEqual('80:fa:5c:0d:43:5e', ip_link['eth0']['mac'])
Example #4
0
    def test_parses_enabled_state(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
        """)
        ip_link = parse_ip_addr(testdata)
        self.assertTrue(ip_link["eth0"]["enabled"])
Example #5
0
def _parse_interfaces(node, data):
    """Return a dict of interfaces keyed by MAC address."""
    interfaces = {}

    # Retrieve informaton from IPADDR_OUTPUT_NAME script
    script_set = node.current_commissioning_script_set
    script_result = script_set.find_script_result(
        script_name=IPADDR_OUTPUT_NAME
    )
    # Only the IP addresses come from ip addr output. While this information
    # is useful for automatically associating a device with a VLAN and subnet
    # it is not necessary for use as users can manually do it.
    if not script_result or script_result.status != SCRIPT_STATUS.PASSED:
        # Pods don't currently have ip addr information, don't warn about it.
        if not node.is_pod:
            logger.error(
                "%s: Unable to discover NIC IP addresses due to missing "
                "passed output from %s" % (node.hostname, IPADDR_OUTPUT_NAME)
            )
        ip_addr_info = {}
    else:
        ip_addr_info = parse_ip_addr(script_result.output)

    network_cards = data.get("network", {}).get("cards", {})
    for card in network_cards:
        for port in card.get("ports", {}):
            mac = port.get("address")
            if mac in (None, SWITCH_OPENBMC_MAC):
                # Ignore loopback (with no MAC) and OpenBMC interfaces on
                # switches which all share the same, hard-coded OpenBMC MAC
                # address.
                continue

            interface = {
                "name": port.get("id"),
                "link_connected": port.get("link_detected"),
                "interface_speed": _parse_interface_speed(port),
                "link_speed": port.get("link_speed", 0),
                "numa_node": card.get("numa_node", 0),
                "vendor": card.get("vendor"),
                "product": card.get("product"),
                "firmware_version": card.get("firmware_version"),
                "sriov_max_vf": card.get("sriov", {}).get("maximum_vfs", 0),
            }
            # Assign the IP addresses to this interface
            link = ip_addr_info.get(interface["name"], {})
            interface["ips"] = link.get("inet", []) + link.get("inet6", [])

            if mac in interfaces:
                raise DuplicateMACs(
                    f"Duplicated MAC address({mac}) on "
                    "{interfaces[mac]['name']} and {interface['name']}"
                )
            else:
                interfaces[mac] = interface

    return interfaces
Example #6
0
    def test_skips_ipv4_link_local(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet 169.254.1.4/16 brd 192.168.0.255 scope global eth0
                valid_lft forever preferred_lft forever
        """)
        ip_link = parse_ip_addr(testdata)
        self.assertIsNone(ip_link['eth0'].get('inet'))
Example #7
0
    def test_skips_ipv6_link_local(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet6 fe80::3e97:eff:fe0e:56dc/64 scope link
                valid_lft forever preferred_lft forever
        """)
        ip_link = parse_ip_addr(testdata)
        self.assertIsNone(ip_link['eth0'].get('inet6'))
Example #8
0
    def test_parses_flags(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
        """)
        ip_link = parse_ip_addr(testdata)
        flags = ip_link['eth0'].get('flags')
        self.assertIsNotNone(flags)
        self.assertThat(set(flags),
                        Equals({'BROADCAST', 'MULTICAST', 'UP', 'LOWER_UP'}))
Example #9
0
    def test_handles_wlan_flags(self):
        testdata = dedent("""
        2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet6 fe80::3e97:eff:fe0e:56dc/64 scope link
                valid_lft forever preferred_lft forever
            inet 192.168.2.112/24 brd 192.168.2.255 scope global dynamic wlan0
                valid_lft forever preferred_lft forever
        """)
        ip_link = parse_ip_addr(testdata)
        inet = ip_link['wlan0'].get('inet')
        self.assertEqual(['192.168.2.112/24'], inet)
Example #10
0
    def test_parses_inet6(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0
                valid_lft forever preferred_lft forever
            inet6 2001:db8:85a3:8d3:1319:8a2e:370:7348/64 scope link
                valid_lft forever preferred_lft forever
        """)
        ip_link = parse_ip_addr(testdata)
        inet = ip_link['eth0'].get('inet6')
        self.assertEqual(['2001:db8:85a3:8d3:1319:8a2e:370:7348/64'], inet)
Example #11
0
    def test_parses_inet(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0
                valid_lft forever preferred_lft forever
            inet6 fe80::3e97:eff:fe0e:56dc/64 scope link
                valid_lft forever preferred_lft forever
        """)
        ip_link = parse_ip_addr(testdata)
        inet = ip_link["eth0"].get("inet")
        self.assertEqual(["192.168.0.3/24"], inet)
Example #12
0
 def test__populates_interface_type_for_each_interface(self):
     # Note: this is more of an end-to-end test, since we call
     # "/sbin/ip addr" on the host running the tests.
     ip_addr_output = check_output(['ip', 'addr'])
     interfaces = parse_ip_addr(ip_addr_output)
     interfaces_with_types = annotate_with_driver_information(interfaces)
     for name in interfaces:
         iface = interfaces_with_types[name]
         self.assertThat(iface, Contains('type'))
         if iface['type'] == 'ethernet.vlan':
             self.expectThat(iface, Contains('vid'))
         elif iface['type'] == 'ethernet.bond':
             self.expectThat(iface, Contains('bonded_interfaces'))
         elif iface['type'] == 'ethernet.bridge':
             self.expectThat(iface, Contains('bridged_interfaces'))
Example #13
0
 def test__populates_interface_type_for_each_interface(self):
     # Note: this is more of an end-to-end test, since we call
     # "/sbin/ip addr" on the host running the tests.
     ip_addr_output = check_output(["ip", "addr"])
     interfaces = parse_ip_addr(ip_addr_output)
     interfaces_with_types = annotate_with_driver_information(interfaces)
     for name in interfaces:
         iface = interfaces_with_types[name]
         self.assertThat(iface, Contains("type"))
         if iface["type"] == "ethernet.vlan":
             self.expectThat(iface, Contains("vid"))
         elif iface["type"] == "ethernet.bond":
             self.expectThat(iface, Contains("bonded_interfaces"))
         elif iface["type"] == "ethernet.bridge":
             self.expectThat(iface, Contains("bridged_interfaces"))
Example #14
0
    def test_parses_settings(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
        """)
        ip_link = parse_ip_addr(testdata)
        settings = ip_link['eth0'].get('settings')
        self.assertIsNotNone(settings)
        self.assertThat(settings, Equals({
            'mtu': '1500',
            'qdisc': 'pfifo_fast',
            'state': 'UP',
            'mode': 'DEFAULT',
            'group': 'default',
            'qlen': '1000',
        }))
Example #15
0
    def test_parses_multiple_interfaces(self):
        testdata = dedent(
            """
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
            inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0
                valid_lft forever preferred_lft forever
            inet6 2001:db8:85a3:8d3:1319:8a2e:370:7350/64 scope link
                valid_lft forever preferred_lft forever
        3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \
mode DORMANT group default qlen 1000
            link/ether 48:51:bb:7a:d5:e2 brd ff:ff:ff:ff:ff:ff
            inet 192.168.0.5/24 brd 192.168.0.255 scope global eth1
                valid_lft forever preferred_lft forever
            inet6 2001:db8:85a3:8d3:1319:8a2e:370:7348/64 scope link
                valid_lft forever preferred_lft forever
            inet6 2001:db8:85a3:8d3:1319:8a2e:370:3645/64 scope global dynamic
                valid_lft forever preferred_lft forever
            inet6 2001:db8:85a3:8d3::1111/64 scope global tentative dynamic
                valid_lft forever preferred_lft forever
            inet6 2620:1:260::1/64 scope global
                valid_lft forever preferred_lft forever
        """
        )
        ip_link = parse_ip_addr(testdata)
        self.assertEqual(2, ip_link["eth0"]["index"])
        self.assertEqual("80:fa:5c:0d:43:5e", ip_link["eth0"]["mac"])
        self.assertEqual(["192.168.0.3/24"], ip_link["eth0"]["inet"])
        self.assertEqual(
            ["2001:db8:85a3:8d3:1319:8a2e:370:7350/64"],
            ip_link["eth0"]["inet6"],
        )
        self.assertEqual(3, ip_link["eth1"]["index"])
        self.assertEqual("48:51:bb:7a:d5:e2", ip_link["eth1"]["mac"])
        self.assertEqual(["192.168.0.5/24"], ip_link["eth1"]["inet"])
        self.assertEqual(
            [
                "2001:db8:85a3:8d3:1319:8a2e:370:7348/64",
                "2001:db8:85a3:8d3:1319:8a2e:370:3645/64",
                "2001:db8:85a3:8d3::1111/64",
                "2620:1:260::1/64",
            ],
            ip_link["eth1"]["inet6"],
        )
Example #16
0
def _parse_interfaces(node, data):
    """Return a dict of interfaces keyed by MAC address."""
    interfaces = {}

    # Retrieve informaton from IPADDR_SCRIPT
    script_set = node.current_commissioning_script_set
    script_result = script_set.find_script_result(
        script_name=IPADDR_OUTPUT_NAME
    )
    if not script_result or script_result.status != SCRIPT_STATUS.PASSED:
        logger.error(
            "%s: Unable to discover NIC IP addresses due to missing "
            "passed output from %s" % (node.hostname, IPADDR_OUTPUT_NAME)
        )
    assert isinstance(script_result.output, bytes)

    ip_addr_info = parse_ip_addr(script_result.output)
    network_cards = data.get("network", {}).get("cards", {})
    for card in network_cards:
        for port in card.get("ports", {}):
            mac = port.get("address")
            if mac in (None, SWITCH_OPENBMC_MAC):
                # Ignore loopback (with no MAC) and OpenBMC interfaces on
                # switches which all share the same, hard-coded OpenBMC MAC
                # address.
                continue

            interface = {
                "name": port.get("id"),
                "link_connected": port.get("link_detected"),
                "interface_speed": _parse_interface_speed(port),
                "link_speed": port.get("link_speed", 0),
                "numa_node": card.get("numa_node", 0),
                "vendor": card.get("vendor"),
                "product": card.get("product"),
                "firmware_version": card.get("firmware_version"),
                "sriov_max_vf": card.get("sriov", {}).get("maximum_vfs", 0),
            }
            # Assign the IP addresses to this interface
            link = ip_addr_info[interface["name"]]
            interface["ips"] = link.get("inet", []) + link.get("inet6", [])

            interfaces[mac] = interface

    return interfaces
Example #17
0
def _parse_interfaces(node, data):
    """Return a dict of interfaces keyed by MAC address."""
    interfaces = {}

    # Retrieve informaton from IPADDR_SCRIPT
    script_set = node.current_commissioning_script_set
    script_result = script_set.find_script_result(
        script_name=IPADDR_OUTPUT_NAME)
    if not script_result or script_result.status != SCRIPT_STATUS.PASSED:
        logger.error('%s: Unable to discover NIC IP addresses due to missing '
                     'passed output from %s' %
                     (node.hostname, IPADDR_OUTPUT_NAME))
    assert isinstance(script_result.output, bytes)

    ip_addr_info = parse_ip_addr(script_result.output)
    network_cards = data.get('network', {}).get('cards', {})
    for card in network_cards:
        for port in card.get('ports', {}):
            mac = port.get('address')
            if mac in (None, SWITCH_OPENBMC_MAC):
                # Ignore loopback (with no MAC) and OpenBMC interfaces on
                # switches which all share the same, hard-coded OpenBMC MAC
                # address.
                continue

            interface = {
                'name': port.get('id'),
                'link_connected': port.get('link_detected'),
                'interface_speed': _parse_interface_speed(port),
                'link_speed': port.get('link_speed'),
                'numa_node': card.get('numa_node', 0),
                'vendor': card.get('vendor'),
                'product': card.get('product'),
                'firmware_version': card.get('firmware_version'),
                'sriov_max_vf': card.get('sriov', {}).get('maximum_vfs', 0),
            }
            # Assign the IP addresses to this interface
            link = ip_addr_info[interface['name']]
            interface['ips'] = link.get('inet', []) + link.get('inet6', [])

            interfaces[mac] = interface

    return interfaces
Example #18
0
    def test_parses_settings(self):
        testdata = dedent("""
        2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast \
state UP mode DEFAULT group default qlen 1000
            link/ether 80:fa:5c:0d:43:5e brd ff:ff:ff:ff:ff:ff
        """)
        ip_link = parse_ip_addr(testdata)
        settings = ip_link["eth0"].get("settings")
        self.assertIsNotNone(settings)
        self.assertThat(
            settings,
            Equals({
                "mtu": "1500",
                "qdisc": "pfifo_fast",
                "state": "UP",
                "mode": "DEFAULT",
                "group": "default",
                "qlen": "1000",
            }),
        )
Example #19
0
def update_node_network_information(node, output, exit_status):
    """Updates the network interfaces from the results of `IPADDR_SCRIPT`.

    Creates and deletes an Interface according to what we currently know about
    this node's hardware.

    If `exit_status` is non-zero, this function returns without doing
    anything.

    """
    if exit_status != 0:
        logger.error(
            "%s: node network information script failed with status: %s." %
            (node.hostname, exit_status))
        return
    assert isinstance(output, bytes)

    # Skip network configuration if set by the user.
    if node.skip_networking:
        # Turn off skip_networking now that the hook has been called.
        node.skip_networking = False
        node.save(update_fields=['skip_networking'])
        return

    # Get the MAC addresses of all connected interfaces.
    ip_addr_info = parse_ip_addr(output)
    current_interfaces = set()
    extended_nic_info = parse_lshw_nic_info(node)

    for link in ip_addr_info.values():
        link_mac = link.get('mac')
        # Ignore loopback interfaces.
        if link_mac is None:
            continue
        elif link_mac == SWITCH_OPENBMC_MAC:
            # Ignore OpenBMC interfaces on switches which all share the same,
            # hard-coded OpenBMC MAC address.
            continue
        else:
            ifname = link['name']
            extra_info = extended_nic_info.get(link_mac, {})
            try:
                interface = PhysicalInterface.objects.get(mac_address=link_mac)
                update_fields = []
                if interface.node is not None and interface.node != node:
                    logger.warning(
                        "Interface with MAC %s moved from node %s to %s. "
                        "(The existing interface will be deleted.)" %
                        (interface.mac_address, interface.node.fqdn,
                         node.fqdn))
                    interface.delete()
                    interface = _create_default_physical_interface(
                        node, ifname, link_mac, **extra_info)
                else:
                    # Interface already exists on this Node, so just update
                    # the name and NIC info.
                    update_fields = []
                    if interface.name != ifname:
                        interface.name = ifname
                        update_fields.append('name')
                    for k, v in extra_info.items():
                        if getattr(interface, k, v) != v:
                            setattr(interface, k, v)
                            update_fields.append(k)
                    if update_fields:
                        interface.save(
                            update_fields=['updated', *update_fields])
            except PhysicalInterface.DoesNotExist:
                interface = _create_default_physical_interface(
                    node, ifname, link_mac, **extra_info)

            current_interfaces.add(interface)
            ips = link.get('inet', []) + link.get('inet6', [])
            interface.update_ip_addresses(ips)
            if 'NO-CARRIER' in link.get('flags', []):
                # This interface is now disconnected.
                if interface.vlan is not None:
                    interface.vlan = None
                    interface.save(update_fields=['vlan', 'updated'])

    # If a machine boots by UUID before commissioning(s390x) no boot_interface
    # will be set as interfaces existed during boot. Set it using the
    # boot_cluster_ip now that the interfaces have been created.
    if node.boot_interface is None and node.boot_cluster_ip is not None:
        subnet = Subnet.objects.get_best_subnet_for_ip(node.boot_cluster_ip)
        if subnet:
            node.boot_interface = node.interface_set.filter(
                id__in=[interface.id for interface in current_interfaces],
                vlan=subnet.vlan).first()
            node.save(update_fields=['boot_interface'])

    # Only configured Interfaces are tested so configuration must be done
    # before regeneration.
    node.set_initial_networking_configuration()

    # XXX ltrager 11-16-2017 - Don't regenerate ScriptResults on controllers.
    # Currently this is not needed saving us 1 database query. However, if
    # commissioning is ever enabled for controllers regeneration will need
    # to be allowed on controllers otherwise network testing may break.
    if node.current_testing_script_set is not None and not node.is_controller:
        # LP: #1731353 - Regenerate ScriptResults before deleting Interfaces.
        # This creates a ScriptResult with proper parameters for each interface
        # on the system. Interfaces no long available will be deleted which
        # causes a casade delete on their assoicated ScriptResults.
        node.current_testing_script_set.regenerate(storage=False, network=True)

    Interface.objects.filter(node=node).exclude(
        id__in=[iface.id for iface in current_interfaces]).delete()
Example #20
0
File: hooks.py Project: pfxuan/maas
def update_node_network_information(node, output, exit_status):
    """Updates the network interfaces from the results of `IPADDR_SCRIPT`.

    Creates and deletes an Interface according to what we currently know about
    this node's hardware.

    If `exit_status` is non-zero, this function returns without doing
    anything.

    """
    if exit_status != 0:
        logger.error(
            "%s: node network information script failed with status: %s." % (
                node.hostname, exit_status))
        return
    assert isinstance(output, bytes)

    # Skip network configuration if set by the user.
    if node.skip_networking:
        # Turn off skip_networking now that the hook has been called.
        node.skip_networking = False
        node.save(update_fields=['skip_networking'])
        return

    # Get the MAC addresses of all connected interfaces.
    ip_addr_info = parse_ip_addr(output)
    current_interfaces = set()
    extended_nic_info = parse_lshw_nic_info(node)

    for link in ip_addr_info.values():
        link_mac = link.get('mac')
        # Ignore loopback interfaces.
        if link_mac is None:
            continue
        elif link_mac == SWITCH_OPENBMC_MAC:
            # Ignore OpenBMC interfaces on switches which all share the same,
            # hard-coded OpenBMC MAC address.
            continue
        else:
            ifname = link['name']
            extra_info = extended_nic_info.get(link_mac, {})
            try:
                interface = PhysicalInterface.objects.get(
                    mac_address=link_mac)
                update_fields = []
                if interface.node is not None and interface.node != node:
                    logger.warning(
                        "Interface with MAC %s moved from node %s to %s. "
                        "(The existing interface will be deleted.)" %
                        (interface.mac_address, interface.node.fqdn,
                         node.fqdn))
                    interface.delete()
                    interface = _create_default_physical_interface(
                        node, ifname, link_mac, **extra_info)
                else:
                    # Interface already exists on this Node, so just update
                    # the name and NIC info.
                    update_fields = []
                    if interface.name != ifname:
                        interface.name = ifname
                        update_fields.append('name')
                    for k, v in extra_info.items():
                        if getattr(interface, k, v) != v:
                            setattr(interface, k, v)
                            update_fields.append(k)
                    if update_fields:
                        interface.save(
                            update_fields=['updated', *update_fields])
            except PhysicalInterface.DoesNotExist:
                interface = _create_default_physical_interface(
                    node, ifname, link_mac, **extra_info)

            current_interfaces.add(interface)
            ips = link.get('inet', []) + link.get('inet6', [])
            interface.update_ip_addresses(ips)
            if 'NO-CARRIER' in link.get('flags', []):
                # This interface is now disconnected.
                if interface.vlan is not None:
                    interface.vlan = None
                    interface.save(update_fields=['vlan', 'updated'])

    for iface in Interface.objects.filter(node=node):
        if iface not in current_interfaces:
            iface.delete()

    # If a machine boots by UUID before commissioning(s390x) no boot_interface
    # will be set as interfaces existed during boot. Set it using the
    # boot_cluster_ip now that the interfaces have been created.
    if node.boot_interface is None and node.boot_cluster_ip is not None:
        subnet = Subnet.objects.get_best_subnet_for_ip(node.boot_cluster_ip)
        if subnet:
            node.boot_interface = node.interface_set.filter(
                vlan=subnet.vlan).first()
            node.save(update_fields=['boot_interface'])
Example #21
0
def update_node_network_information(node, output, exit_status):
    """Updates the network interfaces from the results of `IPADDR_SCRIPT`.

    Creates and deletes an Interface according to what we currently know about
    this node's hardware.

    If `exit_status` is non-zero, this function returns without doing
    anything.

    """
    if exit_status != 0:
        logger.error(
            "%s: node network information script failed with status: %s." %
            (node.hostname, exit_status))
        return
    assert isinstance(output, bytes)

    # Skip network configuration if set by the user.
    if node.skip_networking:
        return

    # Get the MAC addresses of all connected interfaces.
    ip_addr_info = parse_ip_addr(output)
    current_interfaces = set()

    for link in ip_addr_info.values():
        link_mac = link.get('mac')
        # Ignore loopback interfaces.
        if link_mac is None:
            continue
        elif link_mac == SWITCH_OPENBMC_MAC:
            # Ignore OpenBMC interfaces on switches which all share the same,
            # hard-coded OpenBMC MAC address.
            continue
        else:
            ifname = link['name']
            try:
                interface = PhysicalInterface.objects.get(mac_address=link_mac)
                if interface.node is not None and interface.node != node:
                    logger.warning(
                        "Interface with MAC %s moved from node %s to %s. "
                        "(The existing interface will be deleted.)" %
                        (interface.mac_address, interface.node.fqdn,
                         node.fqdn))
                    interface.delete()
                    interface = _create_default_physical_interface(
                        node, ifname, link_mac)
                else:
                    # Interface already exists on this Node, so just update
                    # the name.
                    if interface.name != ifname:
                        interface.name = ifname
                        interface.save(update_fields=['name', 'updated'])
            except PhysicalInterface.DoesNotExist:
                interface = _create_default_physical_interface(
                    node, ifname, link_mac)

            current_interfaces.add(interface)
            ips = link.get('inet', []) + link.get('inet6', [])
            interface.update_ip_addresses(ips)
            if 'NO-CARRIER' in link.get('flags', []):
                # This interface is now disconnected.
                if interface.vlan is not None:
                    interface.vlan = None
                    interface.save(update_fields=['vlan', 'updated'])

    for iface in Interface.objects.filter(node=node):
        if iface not in current_interfaces:
            iface.delete()