Skip to content

Commit

Permalink
Remove storage and volume interface implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Ayush Rangwala <ayush.rangwala@gmail.com>
  • Loading branch information
aayushrangwala committed Jan 17, 2024
1 parent 6e39117 commit e8786ee
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 333 deletions.
273 changes: 0 additions & 273 deletions libcloud/compute/drivers/equinixmetal.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
NodeImage,
NodeDriver,
NodeLocation,
StorageVolume,
VolumeSnapshot,
)
from libcloud.compute.types import Provider, NodeState, InvalidCredsError

Expand Down Expand Up @@ -784,277 +782,6 @@ def ex_disassociate_address(self, address_uuid, include=None):

return result

def list_volumes(self, ex_project_id=None):
if ex_project_id:
return self.ex_list_volumes_for_project(ex_project_id=ex_project_id)

# if project has been specified during driver initialization, then
# return nodes for this project only

if self.project_id:
return self.ex_list_volumes_for_project(ex_project_id=self.project_id)

# In case of Python2 perform requests serially

if not use_asyncio():
nodes = []

for project in self.projects:
nodes.extend(self.ex_list_volumes_for_project(ex_project_id=project.id))

return nodes
# In case of Python3 use asyncio to perform requests in parallel

return self.list_resources_async("volumes")

def ex_list_volumes_for_project(self, ex_project_id, include="plan", page=1, per_page=1000):
params = {"include": include, "page": page, "per_page": per_page}
data = self.connection.request(
"/metal/v1/projects/%s/storage" % (ex_project_id), params=params
).object["volumes"]

return list(map(self._to_volume, data))

def _to_volume(self, data):
return StorageVolume(
id=data["id"], name=data["name"], size=data["size"], driver=self, extra=data
)

def create_volume(
self,
size,
location,
plan="storage_1",
description="",
ex_project_id=None,
locked=False,
billing_cycle=None,
customdata="",
snapshot_policies=None,
**kwargs,
):
"""
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)
:type location: :class:`.NodeLocation`
:return: The newly created volume.
:rtype: :class:`StorageVolume`
"""
path = "/metal/v1/projects/%s/storage" % (ex_project_id or self.projects[0].id)
try:
facility = location.extra["code"]
except AttributeError:
facility = location
params = {"facility": facility, "plan": plan, "size": size, "locked": locked}
params.update(kwargs)

if description:
params["description"] = description

if customdata:
params["customdata"] = customdata

if billing_cycle:
params["billing_cycle"] = billing_cycle

if snapshot_policies:
params["snapshot_policies"] = snapshot_policies
data = self.connection.request(path, params=params, method="POST").object

return self._to_volume(data)

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
res = self.connection.request(path, method="DELETE")

return res.status == httplib.NO_CONTENT

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
params = {"device_id": node.id}
res = self.connection.request(path, params=params, method="POST")

return res.status == httplib.OK

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
attachments = volume.extra["attachments"]
assert len(attachments) > 0, "Volume is not attached to any node"
success = True
result = None

for attachment in attachments:
if not ex_attachment_id or ex_attachment_id in attachment["href"]:
attachment_id = attachment["href"].split("/")[-1]

if ex_node:
node_id = self.ex_describe_attachment(attachment_id)["device"]["href"].split(
"/"
)[-1]

if node_id != ex_node.id:
continue
path = "/metal/v1/storage/attachments/%s" % (ex_attachment_id or attachment_id)
result = self.connection.request(path, method="DELETE")
success = success and result.status == httplib.NO_CONTENT

return result and success

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`
"""
path = "/metal/v1/storage/%s/snapshots" % volume.id
res = self.connection.request(path, method="POST")
assert res.status == httplib.ACCEPTED

return volume.list_snapshots()[-1]

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]
path = "/metal/v1/storage/{}/snapshots/{}".format(volume_id, snapshot.id)
res = self.connection.request(path, method="DELETE")

return res.status == httplib.NO_CONTENT

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`
"""
path = "/metal/v1/storage/%s/snapshots" % volume.id
params = {}

if include:
params["include"] = include
data = self.connection.request(path, params=params).object["snapshots"]

return list(map(self._to_volume_snapshot, data))

def _to_volume_snapshot(self, data):
created = datetime.datetime.strptime(data["created_at"], "%Y-%m-%dT%H:%M:%S")

return VolumeSnapshot(
id=data["id"],
name=data["id"],
created=created,
state=data["status"],
driver=self,
extra=data,
)

def ex_modify_volume(
self,
volume,
description=None,
size=None,
locked=None,
billing_cycle=None,
customdata=None,
):
path = "/metal/v1/storage/%s" % volume.id
params = {}

if description:
params["description"] = description

if size:
params["size"] = size

if locked is not None:
params["locked"] = locked

if billing_cycle:
params["billing_cycle"] = billing_cycle
res = self.connection.request(path, params=params, method="PUT")

return self._to_volume(res.object)

def ex_restore_volume(self, snapshot):
volume_id = snapshot.extra["volume"]["href"].split("/")[-1]
ts = snapshot.extra["timestamp"]
path = "/metal/v1/storage/{}/restore?restore_point={}".format(volume_id, ts)
res = self.connection.request(path, method="POST")

return res.status == httplib.NO_CONTENT

def ex_clone_volume(self, volume, snapshot=None):
path = "/metal/v1/storage/%s/clone" % volume.id

if snapshot:
path += "?snapshot_timestamp=%s" % snapshot.extra["timestamp"]
res = self.connection.request(path, method="POST")

return res.status == httplib.NO_CONTENT

def ex_describe_volume(self, volume_id):
path = "/metal/v1/storage/%s" % volume_id
data = self.connection.request(path).object

return self._to_volume(data)

def ex_describe_attachment(self, attachment_id):
path = "/metal/v1/storage/attachments/%s" % attachment_id
data = self.connection.request(path).object

return data


class Project:
def __init__(self, project):
Expand Down
63 changes: 3 additions & 60 deletions libcloud/test/compute/test_equinixmetal.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,66 +251,6 @@ def test_ex_disassociate_address_with_node(self):

break

def test_list_volumes(self):
volumes = self.driver.list_volumes()
assert len(volumes) == 2
assert len(volumes[0].extra["attachments"]) == 0

def test_create_volume(self):
location = self.driver.list_locations()[0]
volume = self.driver.create_volume(
10,
location,
description="test volume",
plan="storage_1",
ex_project_id="3d27fd13-0466-4878-be22-9a4b5595a3df",
)
assert len(volume.extra["attachments"]) == 0
assert not volume.extra["locked"]

def test_attach_volume(self):
attached = False
volumes = self.driver.ex_list_volumes_for_project(
ex_project_id="3d27fd13-0466-4878-be22-9a4b5595a3df"
)
node = self.driver.ex_list_nodes_for_project(
ex_project_id="3d27fd13-0466-4878-be22-9a4b5595a3df"
)[0]

for vol in volumes:
if len(vol.extra["attachments"]) == 0:
attached = self.driver.attach_volume(node, vol)

break
assert attached

def test_detach_volume(self):
detached = False
volumes = self.driver.ex_list_volumes_for_project(
ex_project_id="3d27fd13-0466-4878-be22-9a4b5595a3df"
)

for vol in volumes:
if len(vol.extra["attachments"]) > 0:
detached = self.driver.detach_volume(vol)

break
assert detached

def test_destroy_volume(self):
destroyed = False
volumes = self.driver.ex_list_volumes_for_project(
ex_project_id="3d27fd13-0466-4878-be22-9a4b5595a3df"
)

for vol in volumes:
if len(vol.extra["attachments"]) == 0:
destroyed = self.driver.destroy_volume(vol)

break
assert destroyed


class EquinixMetalMockHttp(MockHttp):
fixtures = ComputeFileFixtures("equinixmetal")

Expand Down Expand Up @@ -522,6 +462,7 @@ def _metal_v1_ips_aea4ee0c_675f_4b77_8337_8e13b868dd9c(self, method, url, body,
if method == "DELETE":
return (httplib.OK, "", {}, httplib.responses[httplib.OK])

<<<<<<< HEAD
def _metal_v1_projects_3d27fd13_0466_4878_be22_9a4b5595a3df_storage(
self, method, url, body, headers
):
Expand Down Expand Up @@ -570,6 +511,8 @@ def _metal_v1_storage_attachments_2c16a96f_bb4f_471b_8e2e_b5820b9e1603(
if method == "DELETE":
return (httplib.NO_CONTENT, "", {}, httplib.responses[httplib.NO_CONTENT])

=======
>>>>>>> 86ff77e3c (Remove storage and volume interface implementation)

if __name__ == "__main__":
sys.exit(unittest.main())

0 comments on commit e8786ee

Please sign in to comment.