This is an automated email from the ASF dual-hosted git repository.
micafer pushed a commit to branch improve_upload
in repository https://gitbox.apache.org/repos/asf/libcloud.git
The following commit(s) were added to refs/heads/improve_upload by this push:
new 4a9e7d09f Fix to 1.2 version
4a9e7d09f is described below
commit 4a9e7d09f5a747acc5306c056febef4ccc70ae59
Author: Miguel Caballer <[email protected]>
AuthorDate: Fri Jul 3 12:14:17 2026 +0200
Fix to 1.2 version
---
libcloud/common/upcloud.py | 6 +-
libcloud/compute/drivers/upcloud.py | 79 ++++++++--------------
...orage_01d4fcd4-e446-433b-8a9c-551a1284952e.json | 9 +--
.../fixtures/upcloud/api_1_2_storage_create.json | 9 +--
.../fixtures/upcloud/api_1_2_storage_normal.json | 11 +--
libcloud/test/compute/test_upcloud.py | 60 +++++++---------
6 files changed, 57 insertions(+), 117 deletions(-)
diff --git a/libcloud/common/upcloud.py b/libcloud/common/upcloud.py
index 632695335..5b4273da8 100644
--- a/libcloud/common/upcloud.py
+++ b/libcloud/common/upcloud.py
@@ -188,11 +188,7 @@ class UpcloudNodeOperations:
:param node_id: Id of the Node
:type node_id: ``int``
"""
- self.connection.request(
- "1.2/server/{}/start".format(node_id),
- method="POST",
- data=json.dumps({"server": {}}),
- )
+ self.connection.request("1.2/server/{}/start".format(node_id),
method="POST")
def get_node_state(self, node_id):
"""
diff --git a/libcloud/compute/drivers/upcloud.py
b/libcloud/compute/drivers/upcloud.py
index 1f781d19a..36dbf4edd 100644
--- a/libcloud/compute/drivers/upcloud.py
+++ b/libcloud/compute/drivers/upcloud.py
@@ -240,8 +240,6 @@ class UpcloudDriver(NodeDriver):
location=None,
snapshot=None,
ex_tier="maxiops",
- ex_encrypted=False,
- ex_labels=None,
ex_backup_rule=None,
):
"""
@@ -256,17 +254,10 @@ class UpcloudDriver(NodeDriver):
:param location: Which data center to create a volume in. (required)
:type location: :class:`.NodeLocation`
- :param ex_tier: UpCloud storage tier: ``maxiops``, ``standard``, or
- ``hdd``. Default is ``maxiops``. (optional)
+ :param ex_tier: UpCloud storage tier: ``maxiops`` or ``hdd``.
+ Default is ``maxiops``. (optional)
:type ex_tier: ``str``
- :param ex_encrypted: Create the volume encrypted at rest. Default is
- False. (optional)
- :type ex_encrypted: ``bool``
-
- :param ex_labels: Labels for the volume. (optional)
- :type ex_labels: ``list`` of ``dict``
-
:param ex_backup_rule: Backup rule block for automatic backups.
(optional)
:type ex_backup_rule: ``dict``
@@ -284,10 +275,7 @@ class UpcloudDriver(NodeDriver):
"title": name,
"zone": location.id,
"tier": ex_tier,
- "encrypted": "yes" if ex_encrypted else "no",
}
- if ex_labels is not None:
- storage["labels"] = ex_labels
if ex_backup_rule is not None:
storage["backup_rule"] = ex_backup_rule
@@ -327,35 +315,51 @@ class UpcloudDriver(NodeDriver):
Default is False. (optional)
:type ex_boot_disk: ``bool``
- :rtype: :class:`StorageVolume`
+ :rtype: ``bool``
"""
storage_device = {
"type": ex_type,
- "server": node.id,
+ "storage": volume.id,
"boot_disk": "1" if ex_boot_disk else "0",
}
if device is not None:
storage_device["address"] = device
- response = self.connection.request(
- "1.2/storage/{}/attach".format(volume.id),
+ self.connection.request(
+ "1.2/server/{}/storage/attach".format(node.id),
method="POST",
data=json.dumps({"storage_device": storage_device}),
)
- return self._to_volume(response.object["storage"])
+ return True
- def detach_volume(self, volume):
+ def detach_volume(self, volume, ex_node=None, ex_address=None):
"""
Detach a storage volume from its server.
:param volume: Volume to detach.
:type volume: :class:`StorageVolume`
+ :param ex_node: Node where the volume is attached. Required by the
+ UpCloud 1.2 detach endpoint.
+ :type ex_node: :class:`Node`
+
+ :param ex_address: Device address to detach, for example
+ ``scsi:0:0``. Required by the UpCloud 1.2 detach
+ endpoint.
+ :type ex_address: ``str``
+
:rtype: ``bool``
"""
+ if ex_node is None or ex_address is None:
+ raise ValueError(
+ "UpCloud API 1.2 requires `ex_node` and `ex_address` "
+ "when detaching a volume."
+ )
+
self.connection.request(
- "1.2/storage/{}/detach".format(volume.id),
+ "1.2/server/{}/storage/detach".format(ex_node.id),
method="POST",
+ data=json.dumps({"storage_device": {"address": ex_address}}),
)
return True
@@ -401,45 +405,16 @@ class UpcloudDriver(NodeDriver):
)
return True
- def start_node(
- self,
- node,
- ex_host=None,
- ex_avoid_host=None,
- ex_start_type=None,
- ):
+ def start_node(self, node):
"""
Start the given node.
:param node: the node to start
:type node: :class:`Node`
- :param ex_host: Host id to start the node on. Only available for
- private cloud hosts. (optional)
- :type ex_host: ``int``
-
- :param ex_avoid_host: Host id to avoid when starting the node.
- (optional)
- :type ex_avoid_host: ``int``
-
- :param ex_start_type: Start type, ``sync`` or ``async``. (optional)
- :type ex_start_type: ``str``
-
:rtype: ``bool``
"""
- server = {}
- if ex_host is not None:
- server["host"] = ex_host
- if ex_avoid_host is not None:
- server["avoid_host"] = ex_avoid_host
- if ex_start_type is not None:
- server["start_type"] = ex_start_type
-
- self.connection.request(
- "1.2/server/{}/start".format(node.id),
- method="POST",
- data=json.dumps({"server": server}),
- )
+ self.connection.request("1.2/server/{}/start".format(node.id),
method="POST")
return True
def stop_node(self, node, ex_stop_type="hard", ex_timeout=None):
diff --git
a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json
b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json
index 2269ac415..10f3e7738 100644
---
a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json
+++
b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json
@@ -1,17 +1,10 @@
{
"storage": {
"access": "private",
- "encrypted": "yes",
"backup_rule": "",
"backups": {
"backup": []
},
- "labels": [
- {
- "key": "env",
- "value": "test"
- }
- ],
"license": 0,
"servers": {
"server": [
@@ -20,7 +13,7 @@
},
"size": 50,
"state": "online",
- "tier": "standard",
+ "tier": "maxiops",
"title": "data",
"type": "normal",
"uuid": "01d4fcd4-e446-433b-8a9c-551a1284952e",
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_create.json
b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_create.json
index aa16983f1..60dc27094 100644
--- a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_create.json
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_create.json
@@ -1,24 +1,17 @@
{
"storage": {
"access": "private",
- "encrypted": "yes",
"backup_rule": "",
"backups": {
"backup": []
},
- "labels": [
- {
- "key": "env",
- "value": "test"
- }
- ],
"license": 0,
"servers": {
"server": []
},
"size": 50,
"state": "online",
- "tier": "standard",
+ "tier": "maxiops",
"title": "data",
"type": "normal",
"uuid": "01d4fcd4-e446-433b-8a9c-551a1284952e",
diff --git a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_normal.json
b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_normal.json
index 2c274c5d2..1fdc1cbef 100644
--- a/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_normal.json
+++ b/libcloud/test/compute/fixtures/upcloud/api_1_2_storage_normal.json
@@ -3,8 +3,6 @@
"storage": [
{
"access": "private",
- "encrypted": "no",
- "labels": [],
"license": 0,
"size": 10,
"state": "online",
@@ -16,20 +14,13 @@
},
{
"access": "private",
- "encrypted": "yes",
- "labels": [
- {
- "key": "env",
- "value": "test"
- }
- ],
"license": 0,
"servers": {
"server": []
},
"size": 50,
"state": "online",
- "tier": "standard",
+ "tier": "maxiops",
"title": "data",
"type": "normal",
"uuid": "01d4fcd4-e446-433b-8a9c-551a1284952e",
diff --git a/libcloud/test/compute/test_upcloud.py
b/libcloud/test/compute/test_upcloud.py
index 06d5a7c6b..a8ea45cf6 100644
--- a/libcloud/test/compute/test_upcloud.py
+++ b/libcloud/test/compute/test_upcloud.py
@@ -221,7 +221,7 @@ class UpcloudDriverTests(LibcloudTestCase):
"action": "create",
"title": "data",
"size": 25,
- "tier": "standard",
+ "tier": "maxiops",
}
self.driver.create_node(
@@ -256,19 +256,16 @@ class UpcloudDriverTests(LibcloudTestCase):
size=50,
name="data",
location=location,
- ex_tier="standard",
- ex_encrypted=True,
- ex_labels=[{"key": "env", "value": "test"}],
+ ex_tier="maxiops",
)
self.assertEqual(volume.id, "01d4fcd4-e446-433b-8a9c-551a1284952e")
self.assertEqual(volume.name, "data")
self.assertEqual(volume.size, 50)
- self.assertEqual(volume.extra["encrypted"], "yes")
request_storage = UpcloudMockHttp.last_request_body["storage"]
- self.assertEqual(request_storage["tier"], "standard")
- self.assertEqual(request_storage["encrypted"], "yes")
- self.assertEqual(request_storage["labels"], [{"key": "env", "value":
"test"}])
+ self.assertEqual(request_storage["tier"], "maxiops")
+ self.assertNotIn("encrypted", request_storage)
+ self.assertNotIn("labels", request_storage)
def test_create_volume_requires_location(self):
with self.assertRaises(ValueError):
@@ -278,18 +275,26 @@ class UpcloudDriverTests(LibcloudTestCase):
node = self.driver.list_nodes()[0]
volume = self.driver.list_volumes()[1]
- attached_volume = self.driver.attach_volume(node, volume,
device="scsi")
+ success = self.driver.attach_volume(node, volume, device="scsi")
- self.assertIsInstance(attached_volume, StorageVolume)
- self.assertEqual(attached_volume.id, volume.id)
+ self.assertTrue(success)
request_device = UpcloudMockHttp.last_request_body["storage_device"]
- self.assertEqual(request_device["server"], node.id)
+ self.assertEqual(request_device["storage"], volume.id)
self.assertEqual(request_device["address"], "scsi")
self.assertEqual(request_device["boot_disk"], "0")
def test_detach_volume(self):
+ node = self.driver.list_nodes()[0]
+ volume = self.driver.list_volumes()[1]
+ self.assertTrue(self.driver.detach_volume(volume, ex_node=node,
ex_address="scsi:0:0"))
+
+ request_device = UpcloudMockHttp.last_request_body["storage_device"]
+ self.assertEqual(request_device["address"], "scsi:0:0")
+
+ def test_detach_volume_requires_node_and_address(self):
volume = self.driver.list_volumes()[1]
- self.assertTrue(self.driver.detach_volume(volume))
+ with self.assertRaises(ValueError):
+ self.driver.detach_volume(volume)
def test_destroy_volume(self):
volume = self.driver.list_volumes()[1]
@@ -313,24 +318,10 @@ class UpcloudDriverTests(LibcloudTestCase):
def test_start_node(self):
nodes = self.driver.list_nodes()
- success = self.driver.start_node(
- nodes[0],
- ex_host=8055964291,
- ex_avoid_host=7653311107,
- ex_start_type="async",
- )
+ success = self.driver.start_node(nodes[0])
self.assertTrue(success)
- self.assertEqual(
- UpcloudMockHttp.last_request_body,
- {
- "server": {
- "host": 8055964291,
- "avoid_host": 7653311107,
- "start_type": "async",
- }
- },
- )
+ self.assertIsNone(UpcloudMockHttp.last_request_body)
def test_stop_node(self):
nodes = self.driver.list_nodes()
@@ -443,17 +434,18 @@ class UpcloudMockHttp(MockHttp):
body = self.fixtures.load("api_1_2_storage_create.json")
return (httplib.CREATED, body, {}, httplib.responses[httplib.CREATED])
- def _1_2_storage_01d4fcd4_e446_433b_8a9c_551a1284952e_attach(
+ def _1_2_server_00f8c525_7e62_4108_8115_3958df5b43dc_storage_attach(
self, method, url, body, headers
):
self.__class__.last_request_body = json.loads(body)
- body =
self.fixtures.load("api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json")
+ body =
self.fixtures.load("api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
- def _1_2_storage_01d4fcd4_e446_433b_8a9c_551a1284952e_detach(
+ def _1_2_server_00f8c525_7e62_4108_8115_3958df5b43dc_storage_detach(
self, method, url, body, headers
):
- body =
self.fixtures.load("api_1_2_storage_01d4fcd4-e446-433b-8a9c-551a1284952e.json")
+ self.__class__.last_request_body = json.loads(body)
+ body =
self.fixtures.load("api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _1_2_storage_01d4fcd4_e446_433b_8a9c_551a1284952e(self, method, url,
body, headers):
@@ -470,7 +462,7 @@ class UpcloudMockHttp(MockHttp):
return (httplib.OK, body, {}, httplib.responses[httplib.OK])
def _1_2_server_00f8c525_7e62_4108_8115_3958df5b43dc_start(self, method,
url, body, headers):
- self.__class__.last_request_body = json.loads(body)
+ self.__class__.last_request_body = body
body =
self.fixtures.load("api_1_2_server_00f8c525-7e62-4108-8115-3958df5b43dc.json")
return (httplib.OK, body, {}, httplib.responses[httplib.OK])