From 5c147adfb8c79dd341331469952acc67d5373924 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Sat, 25 Jul 2026 03:48:16 -0700 Subject: [PATCH 1/2] fix(digitalocean): include IPv6 addresses in node public/private IPs The DigitalOcean v2 driver's _to_node() only iterated the "v4" networks list, so a droplet's public IPv6 address was never reported in Node.public_ips. Each match also reassigned the list rather than appending, so only the last address of a given type survived. Iterate both the "v4" and "v6" network lists and append, keeping every reported address. Adds a regression test covering a droplet with both an IPv4 and an IPv6 public address, and updates test_list_nodes_success, whose existing fixture already contains a public IPv6 address that was being dropped. Closes #1738 --- CHANGES.rst | 7 +++++ libcloud/compute/drivers/digitalocean.py | 6 ++-- libcloud/test/compute/test_digitalocean_v2.py | 30 ++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 88b983e4bf..2ada299e81 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,13 @@ Common Compute ~~~~~~~ +- [DigitalOcean] Include IPv6 addresses in ``Node.public_ips`` and + ``Node.private_ips``. Only the ``v4`` networks were inspected, and each + address overwrote the previous one, so a droplet's public IPv6 address was + silently dropped. + (GITHUB-1738) + [Sanjay Santhanam - @Sanjays2402] + - [SSH] Support paramiko 4 RSA key support has been removed as of paramiko 4, so only import it diff --git a/libcloud/compute/drivers/digitalocean.py b/libcloud/compute/drivers/digitalocean.py index 761199dcf3..d489bfb2a0 100644 --- a/libcloud/compute/drivers/digitalocean.py +++ b/libcloud/compute/drivers/digitalocean.py @@ -674,11 +674,11 @@ def _to_node(self, data): private_ips = [] public_ips = [] if networks: - for net in networks["v4"]: + for net in networks.get("v4", []) + networks.get("v6", []): if net["type"] == "private": - private_ips = [net["ip_address"]] + private_ips.append(net["ip_address"]) if net["type"] == "public": - public_ips = [net["ip_address"]] + public_ips.append(net["ip_address"]) extra = {} for key in extra_keys: diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index 7ff55ce881..610259645c 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -129,11 +129,39 @@ def test_list_nodes_success(self): nodes = self.driver.list_nodes() self.assertEqual(len(nodes), 1) self.assertEqual(nodes[0].name, "ubuntu-s-1vcpu-1gb-sfo3-01") - self.assertEqual(nodes[0].public_ips, ["128.199.13.158"]) + self.assertEqual( + nodes[0].public_ips, + ["128.199.13.158", "2604:a880:4:1d0::142:9000"], + ) self.assertEqual(nodes[0].extra["image"]["id"], 69463186) self.assertEqual(nodes[0].extra["size_slug"], "s-1vcpu-1gb") self.assertEqual(len(nodes[0].extra["tags"]), 0) + def test_to_node_includes_ipv6_addresses(self): + # Both IPv4 and IPv6 addresses must be reported, and multiple + # addresses of the same type must all be kept. See GITHUB-1738. + data = { + "id": 1, + "name": "ipv6-droplet", + "status": "active", + "created_at": "2020-10-15T13:58:22Z", + "networks": { + "v4": [ + {"ip_address": "10.0.0.1", "type": "private"}, + {"ip_address": "159.223.187.157", "type": "public"}, + ], + "v6": [ + {"ip_address": "2604:a880:400:d0::1c58:e001", "type": "public"}, + ], + }, + } + node = self.driver._to_node(data) + self.assertEqual( + node.public_ips, + ["159.223.187.157", "2604:a880:400:d0::1c58:e001"], + ) + self.assertEqual(node.private_ips, ["10.0.0.1"]) + def test_list_nodes_fills_created_datetime(self): nodes = self.driver.list_nodes() self.assertEqual(nodes[0].created_at, datetime(2020, 10, 15, 13, 58, 22, tzinfo=UTC)) From 5ccda1bde9dd174d25cb3c32d755b885fbbf5646 Mon Sep 17 00:00:00 2001 From: Miguel Caballer Fernandez Date: Thu, 30 Jul 2026 11:01:01 +0200 Subject: [PATCH 2/2] Improve test Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- libcloud/test/compute/test_digitalocean_v2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index 610259645c..84eb5f01d5 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -148,7 +148,9 @@ def test_to_node_includes_ipv6_addresses(self): "networks": { "v4": [ {"ip_address": "10.0.0.1", "type": "private"}, + {"ip_address": "10.0.0.2", "type": "private"}, {"ip_address": "159.223.187.157", "type": "public"}, + {"ip_address": "159.223.187.158", "type": "public"}, ], "v6": [ {"ip_address": "2604:a880:400:d0::1c58:e001", "type": "public"}, @@ -158,9 +160,9 @@ def test_to_node_includes_ipv6_addresses(self): node = self.driver._to_node(data) self.assertEqual( node.public_ips, - ["159.223.187.157", "2604:a880:400:d0::1c58:e001"], + ["159.223.187.157", "159.223.187.158", "2604:a880:400:d0::1c58:e001"], ) - self.assertEqual(node.private_ips, ["10.0.0.1"]) + self.assertEqual(node.private_ips, ["10.0.0.1", "10.0.0.2"]) def test_list_nodes_fills_created_datetime(self): nodes = self.driver.list_nodes()