From fcd0b43b2d08d425c919280622ccd62946eb1351 Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Tue, 7 Nov 2023 20:51:12 +0530 Subject: [PATCH 1/4] Deprecate facility in the favor of metro Signed-off-by: Ayush Rangwala --- libcloud/compute/drivers/__init__.py | 1 + libcloud/compute/drivers/equinixmetal.py | 48 ++++++++++--------- .../fixtures/equinixmetal/facilities.json | 12 ----- .../compute/fixtures/equinixmetal/metros.json | 10 ++++ libcloud/test/compute/test_equinixmetal.py | 5 +- 5 files changed, 39 insertions(+), 37 deletions(-) delete mode 100644 libcloud/test/compute/fixtures/equinixmetal/facilities.json create mode 100644 libcloud/test/compute/fixtures/equinixmetal/metros.json diff --git a/libcloud/compute/drivers/__init__.py b/libcloud/compute/drivers/__init__.py index 90dc411d5b..39aa15df7c 100644 --- a/libcloud/compute/drivers/__init__.py +++ b/libcloud/compute/drivers/__init__.py @@ -33,4 +33,5 @@ "vcloud", "vpsnet", "onapp", + "equinixmetal" ] diff --git a/libcloud/compute/drivers/equinixmetal.py b/libcloud/compute/drivers/equinixmetal.py index 6d48fcada2..abee16c164 100644 --- a/libcloud/compute/drivers/equinixmetal.py +++ b/libcloud/compute/drivers/equinixmetal.py @@ -252,8 +252,12 @@ def ex_list_nodes_for_project(self, ex_project_id, include="plan", page=1, per_p return list(map(self._to_node, data)) def list_locations(self): +<<<<<<< HEAD data = self.connection.request("/metal/v1/facilities").object["facilities"] +======= + data = self.connection.request("/metal/v1/locations/metros").object["metros"] +>>>>>>> ea215b807 (Deprecate facility in the favor of metro) return list(map(self._to_location, data)) def list_images(self): @@ -303,12 +307,15 @@ def create_node( if not ex_project_id: raise Exception("ex_project_id needs to be specified") - facility = location.extra["code"] + location_code = location.extra["code"] + if not self._valid_location: + raise ValueError("Failed to create node: valid parameter metro [code] is required in the input") + params = { "hostname": name, "plan": size.id, "operating_system": image.id, - "facility": facility, + "metro": location_code, "include": "plan", "billing_cycle": "hourly", } @@ -493,6 +500,8 @@ def _to_node(self, data): if "facility" in data: extra["facility"] = data["facility"] + if "metro" in data and data["metro"] is not None: + extra["metro"] = data["metro"] for key in extra_keys: if key in data: @@ -534,8 +543,8 @@ def _to_size(self, data): except KeyError: cpus = None regions = [ - region.get("href").replace("/metal/v1/facilities/", "") - for region in data.get("available_in", []) + region.get("href").replace("/metal/v1/locations/metros", "") + for region in data.get("available_in_metros", []) ] extra = { "description": data["description"], @@ -752,8 +761,7 @@ def ex_request_address_reservation( } if location_id: - params["facility"] = location_id - + params["metro"] = location_id if comments: params["comments"] = comments @@ -835,10 +843,8 @@ def create_volume( ): """ Create a new volume. - :param size: Size of volume in gigabytes (required) :type size: ``int`` - :param location: Which data center to create a volume in. If empty, undefined behavior will be selected. (optional) @@ -872,10 +878,8 @@ def create_volume( def destroy_volume(self, volume): """ Destroys a storage volume. - :param volume: Volume to be destroyed :type volume: :class:`StorageVolume` - :rtype: ``bool`` """ path = "/metal/v1/storage/%s" % volume.id @@ -886,13 +890,10 @@ def destroy_volume(self, volume): def attach_volume(self, node, volume): """ Attaches volume to node. - :param node: Node to attach volume to. :type node: :class:`.Node` - :param volume: Volume to attach. :type volume: :class:`.StorageVolume` - :rytpe: ``bool`` """ path = "/metal/v1/storage/%s/attachments" % volume.id @@ -904,14 +905,11 @@ def attach_volume(self, node, volume): def detach_volume(self, volume, ex_node=None, ex_attachment_id=""): """ Detaches a volume from a node. - :param volume: Volume to be detached :type volume: :class:`.StorageVolume` - :param ex_attachment_id: Attachment id to be detached, if empty detach all attachments :type name: ``str`` - :rtype: ``bool`` """ path = "/metal/v1/storage/%s/attachments" % volume.id @@ -940,10 +938,8 @@ def detach_volume(self, volume, ex_node=None, ex_attachment_id=""): def create_volume_snapshot(self, volume, name=""): """ Create a new volume snapshot. - :param volume: Volume to create a snapshot for :type volume: class:`StorageVolume` - :return: The newly created volume snapshot. :rtype: :class:`VolumeSnapshot` """ @@ -956,10 +952,8 @@ def create_volume_snapshot(self, volume, name=""): def destroy_volume_snapshot(self, snapshot): """ Delete a volume snapshot - :param snapshot: volume snapshot to delete :type snapshot: class:`VolumeSnapshot` - :rtype: ``bool`` """ volume_id = snapshot.extra["volume"]["href"].split("/")[-1] @@ -971,10 +965,8 @@ def destroy_volume_snapshot(self, snapshot): def list_volume_snapshots(self, volume, include=""): """ List snapshots for a volume. - :param volume: Volume to list snapshots for :type volume: class:`StorageVolume` - :return: List of volume snapshots. :rtype: ``list`` of :class: `VolumeSnapshot` """ @@ -988,8 +980,12 @@ def list_volume_snapshots(self, volume, include=""): return list(map(self._to_volume_snapshot, data)) def _to_volume_snapshot(self, data): +<<<<<<< HEAD created = datetime.datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S") +======= + created = datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S") +>>>>>>> ea215b807 (Deprecate facility in the favor of metro) return VolumeSnapshot( id=data["id"], name=data["id"], @@ -1055,6 +1051,14 @@ def ex_describe_attachment(self, attachment_id): return data + def _valid_location(self, metro_code): + if metro_code == None or metro_code == "": + return False + metros = self.connection.request("/metal/v1/locations/metros").object["metros"] + for metro in metros: + if metro["code"] == metro_code: + return True + return False class Project: def __init__(self, project): diff --git a/libcloud/test/compute/fixtures/equinixmetal/facilities.json b/libcloud/test/compute/fixtures/equinixmetal/facilities.json deleted file mode 100644 index 4b0014c4eb..0000000000 --- a/libcloud/test/compute/fixtures/equinixmetal/facilities.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "facilities": [ - { - "id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09", - "name": "Parsippany, NJ", - "code": "ewr1", - "features": ["baremetal"], - "address": null, - "href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09" - } - ] -} diff --git a/libcloud/test/compute/fixtures/equinixmetal/metros.json b/libcloud/test/compute/fixtures/equinixmetal/metros.json new file mode 100644 index 0000000000..a2fa6c804d --- /dev/null +++ b/libcloud/test/compute/fixtures/equinixmetal/metros.json @@ -0,0 +1,10 @@ +{ + "metros": [ + { + "id": "d3d6b29f-042d-43b7-b3ce-0bf53d5754ca", + "name": "Dallas", + "code": "da", + "country": "US" + } + ] +} diff --git a/libcloud/test/compute/test_equinixmetal.py b/libcloud/test/compute/test_equinixmetal.py index 9c7f3b9288..634b6af2bf 100644 --- a/libcloud/test/compute/test_equinixmetal.py +++ b/libcloud/test/compute/test_equinixmetal.py @@ -314,9 +314,8 @@ def test_destroy_volume(self): class EquinixMetalMockHttp(MockHttp): fixtures = ComputeFileFixtures("equinixmetal") - def _metal_v1_facilities(self, method, url, body, headers): - body = self.fixtures.load("facilities.json") - + def _metal_v1_metros(self, method, url, body, headers): + body = self.fixtures.load("metros.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) def _metal_v1_plans(self, method, url, body, headers): From adf831d7d9c9fd5393762c72e9cd3596afa33782 Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Tue, 21 Nov 2023 22:13:22 +0530 Subject: [PATCH 2/4] fix: lint issues Signed-off-by: Ayush Rangwala --- libcloud/compute/drivers/equinixmetal.py | 1 + libcloud/test/compute/test_equinixmetal.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libcloud/compute/drivers/equinixmetal.py b/libcloud/compute/drivers/equinixmetal.py index abee16c164..ef87183e87 100644 --- a/libcloud/compute/drivers/equinixmetal.py +++ b/libcloud/compute/drivers/equinixmetal.py @@ -843,6 +843,7 @@ def create_volume( ): """ Create a new volume. + :param size: Size of volume in gigabytes (required) :type size: ``int`` :param location: Which data center to create a volume in. If diff --git a/libcloud/test/compute/test_equinixmetal.py b/libcloud/test/compute/test_equinixmetal.py index 634b6af2bf..2ce0132e69 100644 --- a/libcloud/test/compute/test_equinixmetal.py +++ b/libcloud/test/compute/test_equinixmetal.py @@ -314,7 +314,7 @@ def test_destroy_volume(self): class EquinixMetalMockHttp(MockHttp): fixtures = ComputeFileFixtures("equinixmetal") - def _metal_v1_metros(self, method, url, body, headers): + def _metal_v1_locations_metros(self, method, url, body, headers): body = self.fixtures.load("metros.json") return (httplib.OK, body, {}, httplib.responses[httplib.OK]) From 43ca4b47cf5775d3e552a0d6d2bc27b9779ff839 Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Fri, 19 Jan 2024 23:32:56 +0530 Subject: [PATCH 3/4] Fix: merge conflicts Signed-off-by: Ayush Rangwala --- libcloud/compute/drivers/equinixmetal.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libcloud/compute/drivers/equinixmetal.py b/libcloud/compute/drivers/equinixmetal.py index ef87183e87..6da37d7042 100644 --- a/libcloud/compute/drivers/equinixmetal.py +++ b/libcloud/compute/drivers/equinixmetal.py @@ -252,12 +252,7 @@ def ex_list_nodes_for_project(self, ex_project_id, include="plan", page=1, per_p return list(map(self._to_node, data)) def list_locations(self): -<<<<<<< HEAD - data = self.connection.request("/metal/v1/facilities").object["facilities"] - -======= data = self.connection.request("/metal/v1/locations/metros").object["metros"] ->>>>>>> ea215b807 (Deprecate facility in the favor of metro) return list(map(self._to_location, data)) def list_images(self): @@ -981,12 +976,8 @@ def list_volume_snapshots(self, volume, include=""): return list(map(self._to_volume_snapshot, data)) def _to_volume_snapshot(self, data): -<<<<<<< HEAD created = datetime.datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S") -======= - created = datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S") ->>>>>>> ea215b807 (Deprecate facility in the favor of metro) return VolumeSnapshot( id=data["id"], name=data["id"], From 92a86c21dcec4936eeb64a957d6ab90c6ad1cc1d Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Fri, 19 Jan 2024 23:37:54 +0530 Subject: [PATCH 4/4] Add back spaces Signed-off-by: Ayush Rangwala --- libcloud/compute/drivers/equinixmetal.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libcloud/compute/drivers/equinixmetal.py b/libcloud/compute/drivers/equinixmetal.py index 6da37d7042..6a21b2a626 100644 --- a/libcloud/compute/drivers/equinixmetal.py +++ b/libcloud/compute/drivers/equinixmetal.py @@ -253,6 +253,7 @@ def ex_list_nodes_for_project(self, ex_project_id, include="plan", page=1, per_p def list_locations(self): data = self.connection.request("/metal/v1/locations/metros").object["metros"] + return list(map(self._to_location, data)) def list_images(self): @@ -757,6 +758,7 @@ def ex_request_address_reservation( if location_id: params["metro"] = location_id + if comments: params["comments"] = comments @@ -841,6 +843,7 @@ def create_volume( :param size: Size of volume in gigabytes (required) :type size: ``int`` + :param location: Which data center to create a volume in. If empty, undefined behavior will be selected. (optional) @@ -874,8 +877,10 @@ def create_volume( def destroy_volume(self, volume): """ Destroys a storage volume. + :param volume: Volume to be destroyed :type volume: :class:`StorageVolume` + :rtype: ``bool`` """ path = "/metal/v1/storage/%s" % volume.id @@ -886,10 +891,13 @@ def destroy_volume(self, volume): def attach_volume(self, node, volume): """ Attaches volume to node. + :param node: Node to attach volume to. :type node: :class:`.Node` + :param volume: Volume to attach. :type volume: :class:`.StorageVolume` + :rytpe: ``bool`` """ path = "/metal/v1/storage/%s/attachments" % volume.id @@ -901,11 +909,14 @@ def attach_volume(self, node, volume): def detach_volume(self, volume, ex_node=None, ex_attachment_id=""): """ Detaches a volume from a node. + :param volume: Volume to be detached :type volume: :class:`.StorageVolume` + :param ex_attachment_id: Attachment id to be detached, if empty detach all attachments :type name: ``str`` + :rtype: ``bool`` """ path = "/metal/v1/storage/%s/attachments" % volume.id @@ -934,8 +945,10 @@ def detach_volume(self, volume, ex_node=None, ex_attachment_id=""): def create_volume_snapshot(self, volume, name=""): """ Create a new volume snapshot. + :param volume: Volume to create a snapshot for :type volume: class:`StorageVolume` + :return: The newly created volume snapshot. :rtype: :class:`VolumeSnapshot` """ @@ -948,8 +961,10 @@ def create_volume_snapshot(self, volume, name=""): def destroy_volume_snapshot(self, snapshot): """ Delete a volume snapshot + :param snapshot: volume snapshot to delete :type snapshot: class:`VolumeSnapshot` + :rtype: ``bool`` """ volume_id = snapshot.extra["volume"]["href"].split("/")[-1] @@ -961,8 +976,10 @@ def destroy_volume_snapshot(self, snapshot): def list_volume_snapshots(self, volume, include=""): """ List snapshots for a volume. + :param volume: Volume to list snapshots for :type volume: class:`StorageVolume` + :return: List of volume snapshots. :rtype: ``list`` of :class: `VolumeSnapshot` """