blob: 479ea0b44515860bab05aaf797353b4dc5924ce6 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from libcloud.compute.base import (
Node,
KeyPair,
NodeImage,
NodeState,
NodeDriver,
NodeLocation,
StorageVolume,
VolumeSnapshot,
StorageVolumeState,
)
from libcloud.utils.iso8601 import parse_date
from libcloud.common.gridscale import GridscaleBaseDriver, GridscaleConnection
from libcloud.compute.providers import Provider
class GridscaleIp:
"""
Ip Object
:param id: uuid
:type id: ``str``
:param family: family of ip (v4 or v6)
:type family: ``str``
:param prefix: prefix of ip
:type prefix: ``str``
:param ip_address: Ip address
:type ip_address: ``str``
:param create_time: Time ip was created
:type create_time: ``str``
"""
def __init__(self, id, family, prefix, create_time, address, extra=None):
self.id = id
self.family = family
self.prefix = prefix
self.create_time = create_time
self.ip_address = address
self.extra = extra or {}
def __repr__(self):
return "Ip: id={}, family={}, prefix={}, create_time={}, " "ip_address={}".format(
self.id, self.family, self.prefix, self.create_time, self.ip_address
)
class GridscaleNetwork:
"""
Network Object
:param id: uuid
:type id: ``str``
:param name: Name of Network
:type name: ``str``
:param status: Network status
:type status: ``str``
:param relations: object related to network
:type relations: ``object``
:param create_time: Time Network was created
:type create_time: ``str``
"""
def __init__(self, id, name, status, create_time, relations):
self.id = id
self.name = name
self.status = status
self.create_time = create_time
self.relations = relations
def __repr__(self):
return "Network: id={}, name={}, status={}, create_time={}, " "relations={}".format(
self.id, self.name, self.status, self.create_time, self.relations
)
class GridscaleNodeDriver(GridscaleBaseDriver, NodeDriver):
"""
create and entry in libcloud/compute/providers for gridscale
"""
connectionCls = GridscaleConnection
type = Provider.GRIDSCALE
name = "Gridscale"
api_name = "gridscale"
website = "https://gridscale.io"
features = {"create_node": ["ssh_key"]}
def __init__(self, user_id, key, **kwargs):
super().__init__(user_id, key, **kwargs)
def list_nodes(self):
"""
List all nodes.
:return: List of node objects
:rtype: ``list`` of :class:`.Node`
"""
result = self._sync_request(data=None, endpoint="objects/servers/")
nodes = []
for key, value in self._get_response_dict(result).items():
node = self._to_node(value)
nodes.append(node)
return sorted(nodes, key=lambda sort: sort.created_at)
def list_locations(self):
"""
List all available data centers.
:return: List of node location objects
:rtype: ``list`` of :class:`.NodeLocation`
"""
locations = []
result = self._sync_request(endpoint="objects/locations/")
for key, value in self._get_response_dict(result).items():
location = self._to_location(value)
locations.append(location)
return sorted(locations, key=lambda nod: nod.id)
def list_volumes(self):
"""
List all volumes.
:return: List of StorageVolume object
:rtype: ``list`` of :class:`.StorageVolume`
"""
volumes = []
result = self._sync_request(endpoint="objects/storages/")
for key, value in self._get_response_dict(result).items():
volume = self._to_volume(value)
volumes.append(volume)
return sorted(volumes, key=lambda sort: sort.extra["create_time"])
def ex_list_networks(self):
"""
List all networks.
:return: List of objects.
:rtype: ``list`` of :class:`.GridscaleNetwork`
"""
networks = []
result = self._sync_request(endpoint="objects/networks/")
for key, value in self._get_response_dict(result).items():
network = self._to_network(value)
networks.append(network)
return sorted(networks, key=lambda sort: sort.create_time)
def list_volume_snapshots(self, volume):
"""
Lists all snapshots for storage volume.
:param volume: storage the snapshot is attached to
:type volume: :class:`.StorageVolume`
:return: Snapshots
:rtype: ``list`` of :class:`.VolumeSnapshot`
"""
snapshots = []
result = self._sync_request(endpoint="objects/storages/" "{}/snapshots".format(volume.id))
for key, value in self._get_response_dict(result).items():
snapshot = self._to_volume_snapshot(value)
snapshots.append(snapshot)
return sorted(snapshots, key=lambda snapshot: snapshot.created)
def ex_list_ips(self):
"""
Lists all IPs available.
:return: List of IP objects.
:rtype: ``list`` of :class:`.GridscaleIp`
"""
ips = []
result = self._sync_request(endpoint="objects/ips/")
for key, value in self._get_response_dict(result).items():
ip = self._to_ip(value)
ips.append(ip)
return ips
def list_images(self):
"""
List images.
:return: List of node image objects
:rtype: ``list`` of :class:`.NodeImage`
"""
templates = []
result = self._sync_request(endpoint="objects/templates")
for key, value in self._get_response_dict(result).items():
template = self._to_node_image(value)
templates.append(template)
return sorted(templates, key=lambda sort: sort.name)
def create_node(self, name, size, image, location, ex_ssh_key_ids=None, **kwargs):
"""
Create a simple node with a name, cores, memory at the designated
location.
:param name: Name of the server.
:type name: ``str``
:param size: Nodesize object.
:type size: :class:`.NodeSize`
:param image: OS image to attach to the storage.
:type image: :class:`.GridscaleTemplate`
:param location: The data center to create a node in.
:type location: :class:`.NodeLocation`
:keyword ex_ssh_key_ids: List of SSH key IDs to add to the server.
:type ex_ssh_key_ids: ``list`` of ``str``
:return: The newly created Node.
:rtype: :class:`.Node`
"""
if size.ram % 1024 != 0:
raise Exception("Value not accepted. Use a multiple of 1024 e.g." "1024, 2048, 3072...")
data = {
"name": name,
"cores": size.extra["cores"],
"memory": int(size.ram / 1024),
"location_uuid": location.id,
}
self.connection.async_request("objects/servers/", data=data, method="POST")
node = self._to_node(
self._get_resource(
"servers", self.connection.poll_response_initial.object["object_uuid"]
)
)
volume = self._create_volume_from_template(
name=image.extra["ostype"],
size=size.disk,
location=location,
template={"template_uuid": image.id, "sshkeys": ex_ssh_key_ids},
)
ip = self.ex_create_ip(4, location, name + "_ip")
self.attach_volume(node, volume)
self.ex_link_ip_to_node(node, ip)
self.ex_link_network_to_node(node, self.ex_list_networks()[0])
self.ex_start_node(node)
return self._to_node(self._get_resource("servers", node.id))
def ex_create_ip(self, family, location, name):
"""
Create either an ip_v4 ip or a ip_v6.
:param family: Defines if the ip is v4 or v6 with int 4 or int 6.
:type family: ``int``
:param location: Defines which datacenter the created ip
responds with.
:type location: :class:`.NodeLocation`
:param name: Name of your Ip.
:type name: ``str``
:return: Ip
:rtype: :class:`.GridscaleIp`
"""
self.connection.async_request(
"objects/ips/",
data={"name": name, "family": family, "location_uuid": location.id},
method="POST",
)
return self._to_ip(
self._get_resource("ips", self.connection.poll_response_initial.object["object_uuid"])
)
def ex_create_networks(self, name, location):
"""
Create a network at the data center location.
:param name: Name of the network.
:type name: ``str``
:param location: Location.
:type location: :class:`.NodeLocation`
:return: Network.
:rtype: :class:`.GridscaleNetwork`
"""
self.connection.async_request(
"objects/networks",
data={"name": name, "location_uuid": location.id},
method="POST",
)
return self._to_network(
self._get_resource(
"network", self.connection.poll_response_initial.object["object_uuid"]
)
)
def create_volume(self, size, name, location=None, snapshot=None):
"""
Create a new volume.
:param size: Integer in GB.
:type size: ``int``
:param name: Name of the volume.
:type name: ``str``
:param location: The server location.
:type location: :class:`.NodeLocation`
:param snapshot: Snapshot from which to create the new
volume. (optional)
:type snapshot: :class:`.VolumeSnapshot`
:return: Newly created StorageVolume.
:rtype: :class:`.StorageVolume`
"""
return self._create_volume_from_template(size, name, location)
def _create_volume_from_template(self, size, name, location=None, template=None):
"""
create Storage
:param name: name of your Storage unit
:type name: ``str``
:param size: Integer in GB.
:type size: ``int``
:param location: your server location
:type location: :class:`.NodeLocation`
:param template: template to shape the storage capacity to
:type template: ``dict``
:return: newly created StorageVolume
:rtype: :class:`.GridscaleVolumeStorage`
"""
template = template
self.connection.async_request(
"objects/storages/",
data={
"name": name,
"capacity": size,
"location_uuid": location.id,
"template": template,
},
method="POST",
)
return self._to_volume(
self._get_resource(
"storages", self.connection.poll_response_initial.object["object_uuid"]
)
)
def create_volume_snapshot(self, volume, name):
"""
Creates a snapshot of the current state of your volume,
you can rollback to.
:param volume: Volume you want to create a snapshot of.
:type volume: :class:`.StorageVolume`
:param name: Name of the snapshot.
:type name: ``str``
:return: VolumeSnapshot.
:rtype: :class:`.VolumeSnapshot`
"""
self.connection.async_request(
"objects/storages/{}/snapshots".format(volume.id),
data={"name": name},
method="POST",
)
return self._to_volume_snapshot(
self._get_resource(
"storages/{}/snapshots".format(volume.id),
self.connection.poll_response_initial.object["object_uuid"],
)
)
def create_image(self, node, name):
"""
Creates an image from a node object.
:param node: Node to run the task on.
:type node: :class:`.Node`
:param name: Name for new image.
:type name: ``str``
:return: NodeImage.
:rtype: :class:`.NodeImage`
"""
storage_dict = node.extra["relations"]["storages"][0]
snapshot_uuid = ""
if storage_dict["bootdevice"] is True:
self.connection.async_request(
"objects/storages/{}/snapshots/".format(storage_dict["object_uuid"]),
data={"name": name + "_snapshot"},
method="POST",
)
snapshot_uuid = self.connection.poll_response_initial.object["object_uuid"]
self.connection.async_request(
"objects/templates/",
data={"name": name, "snapshot_uuid": snapshot_uuid},
method="POST",
)
snapshot_dict = self._get_response_dict(
self._sync_request(
endpoint="objects/storages/{}/snapshots/{}".format(
storage_dict["object_uuid"], snapshot_uuid
)
)
)
self.destroy_volume_snapshot(self._to_volume_snapshot(snapshot_dict))
return self._to_node_image(
self._get_resource(
"templates", self.connection.poll_response_initial.object["object_uuid"]
)
)
def destroy_node(self, node, ex_destroy_associated_resources=False):
"""
Destroy node.
:param node: Node object.
:type node: :class:`.Node`
:param ex_destroy_associated_resources: True to destroy associated
resources such as storage volumes and IPs.
:type ex_destroy_associated_resources: ``bool``
:return: True if the destroy was successful, otherwise False.
:rtype: ``bool``
"""
if ex_destroy_associated_resources:
associated_volumes = self.ex_list_volumes_for_node(node=node)
associated_ips = self.ex_list_ips_for_node(node=node)
# 1. Delete the server itself
result = self._sync_request(endpoint="objects/servers/{}".format(node.id), method="DELETE")
# 2. Destroy associated resouces (if requested)
if ex_destroy_associated_resources:
for volume in associated_volumes:
self.destroy_volume(volume=volume)
for ip in associated_ips:
self.ex_destroy_ip(ip=ip)
return result.status == 204
def destroy_volume(self, volume):
"""
Delete volume.
:param volume: Volume to be destroyed.
:type volume: :class:`.StorageVolume`
:return: True if the destroy was successful, otherwise False.
:rtype: ``bool``
"""
result = self._sync_request(
endpoint="objects/storages/{}".format(volume.id), method="DELETE"
)
return result.status == 204
def ex_destroy_ip(self, ip):
"""
Delete an ip.
:param ip: IP object.
:type ip: :class:`.GridscaleIp`
:return: ``True`` if delete_image was successful, ``False`` otherwise.
:rtype: ``bool``
"""
result = self._sync_request(endpoint="objects/ips/{}".format(ip.id), method="DELETE")
return result.status == 204
def destroy_volume_snapshot(self, snapshot):
"""
Destroy a snapshot.
:param snapshot: The snapshot to delete.
:type snapshot: :class:'.VolumeSnapshot`
:return: True if the destroy was successful, otherwise False.
:rtype: ``bool``
"""
result = self._sync_request(
endpoint="objects/storages/"
"{}/snapshots/{}/".format(snapshot.extra["parent_uuid"], snapshot.id),
method="DELETE",
)
return result.status == 204
def ex_destroy_network(self, network):
"""
Delete network.
:param network: Network object.
:type network: :class:`.GridscaleNetwork`
:return: ``True`` if destroyed successfully, otherwise ``False``
:rtype: ``bool``
"""
result = self._sync_request(
endpoint="objects/networks/{}".format(network.id), method="DELETE"
)
return result.status == 204
def delete_image(self, node_image):
"""
Destroy an image.
:param node_image: Node image object.
:type node_image: :class:`.NodeImage`
:return: True if the destroy was successful, otherwise False
:rtype: ``bool``
"""
result = self._sync_request(
endpoint="objects/templates/{}".format(node_image.id), method="DELETE"
)
return result.status == 204
def ex_rename_node(self, node, name):
"""
Modify node name.
:param name: New node name.
:type name: ``str``
:param node: Node
:type node: :class:`.Node`
:return: ``True`` or ``False``
:rtype: ``bool``
"""
result = self._sync_request(
data={"name": name},
endpoint="objects/servers/{}".format(node.id),
method="PATCH",
)
return result.status == 204
def ex_rename_volume(self, volume, name):
"""
Modify storage volume name
:param volume: Storage.
:type volume: :class:.`StorageVolume`
:param name: New storage name.
:type name: ``str``
:return: ``True`` or ``False``
:rtype: ``bool``
"""
result = self._sync_request(
data={"name": name},
endpoint="objects/storages/{}".format(volume.id),
method="PATCH",
)
return result.status == 204
def ex_rename_network(self, network, name):
"""
Modify networks name.
:param network: Network.
:type network: :class:`.GridscaleNetwork`
:param name: New network name.
:type name: ``str``
:return: ``True`` or ``False``
:rtype: ``bool``
"""
result = self._sync_request(
data={"name": name},
endpoint="objects/networks/{}".format(network.id),
method="PATCH",
)
return result.status == 204
def reboot_node(self, node, ex_sleep_interval=3):
"""
Reboot a node.
:param node: Node object.
:type node: :class:`.Node`
:return: True if the reboot was successful, otherwise False.
:rtype: ``bool``
:keyword ex_sleep_interval: time to let the shutdown process finish
:type ex_sleep_interval: ``int``
"""
if node.extra["power"] is True:
data = dict({"power": False})
self._sync_request(
data=data,
endpoint="objects/servers/{}/power".format(node.id),
method="PATCH",
)
time.sleep(ex_sleep_interval)
data = dict({"power": True})
self._sync_request(
data=data,
endpoint="objects/servers/{}/power".format(node.id),
method="PATCH",
)
return True
else:
return False
def import_key_pair_from_string(self, name, key_material):
data = {"name": name, "sshkey": key_material}
result = self._sync_request(endpoint="objects/sshkeys/", method="POST", data=data)
key = self._to_key(result.object, name=name, sshkey=key_material)
return key
def list_key_pairs(self):
"""
List all the available key pair objects.
:rtype: ``list``of :class:`.KeyPair` objects
"""
keys = []
result = self._sync_request(endpoint="objects/sshkeys/")
for key, value in self._get_response_dict(result).items():
key = self._to_key(value)
keys.append(key)
return keys
def get_image(self, image_id):
"""
Get an image based on an image_id.
:param image_id: Image identifier.
:type image_id: ``str``
:return: A NodeImage object.
:rtype: :class:`.NodeImage`
"""
response_dict = self._get_response_dict(
self._sync_request(endpoint="/objects/templates/{}".format(image_id))
)
return self._to_node_image(response_dict)
def start_node(self, node):
result = self._sync_request(
data={"power": True},
endpoint="objects/servers/{}/power".format(node.id),
method="PATCH",
)
return result.status == 204
def ex_start_node(self, node):
# NOTE: This method is here for backward compatibility reasons after
# this method was promoted to be part of the standard compute API in
# Libcloud v2.7.0
return self.start_node(node=node)
def ex_link_isoimage_to_node(self, node, isoimage):
"""
link and isoimage to a node
:param node: Node you want to link the iso image to
:type node: ``object``
:param isoimage: isomiage you want to link
:type isoimage: ``object``
:return: None -> success
:rtype: ``None``
"""
result = self._sync_request(
data={"object_uuid": isoimage.id},
endpoint="objects/servers/{}/isoimages/".format(node.id),
method="POST",
)
return result
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``
"""
result = self._sync_request(
data={"object_uuid": volume.id},
endpoint="objects/servers/{}/storages/".format(node.id),
method="POST",
)
return result.status == 204
def ex_link_network_to_node(self, node, network):
"""
Link a network to a node.
:param node: Node object to link networks to.
:type node: :class:`.Node`
:param network: Network you want to link.
:type network: :class:`.GridscaleNetwork`
:return: ``True`` if linked sucessfully, otherwise ``False``
:rtype: ``bool``
"""
result = self._sync_request(
data={"object_uuid": network.id},
endpoint="objects/servers/{}/networks/".format(node.id),
method="POST",
)
return result.status == 204
def ex_link_ip_to_node(self, node, ip):
"""
links a existing ip with a node
:param node: node object
:type node: ``object``
:param ip: ip object
:type ip: ``object``
:return: Request ID
:rtype: ``str``
"""
result = self._sync_request(
data={"object_uuid": ip.id},
endpoint="objects/servers/{}/ips/".format(node.id),
method="POST",
)
return result
def ex_unlink_isoimage_from_node(self, node, isoimage):
"""
unlink isoimages from server
:param node: node you want to unlink the image from
:type node: ``object``
:param isoimage: isoimage you want to unlink
:type isoimage: ``object``
:return: None -> success
:rtype: ``None``
"""
result = self._sync_request(
endpoint="objects/servers/{}/isoimages/{}".format(node.id, isoimage.id),
method="DELETE",
)
return result
def ex_unlink_ip_from_node(self, node, ip):
"""
unlink ips from server
:param node: node you want to unlink the ip from
:type node: ``object``
:param ip: the ip you want to unlink
:type ip: ``object``
:return: None -> success
:rtype: ``None``
"""
result = self._sync_request(
endpoint="objects/servers/{}/ips/{}".format(node.id, ip.id), method="DELETE"
)
return result
def ex_unlink_network_from_node(self, node, network):
"""
Unlink network from node.
:param node: Node you want to unlink from network.
:type node: :class:`.Node`
:param network: Network you want to unlink.
:type network: :class:`.GridscaleNetwork
:return: ``True`` if unlink was successful, otherwise ``False``
:rtype: ``bool``
"""
result = self._sync_request(
endpoint="objects/servers/{}/networks/{}".format(node.id, network.id),
method="DELETE",
)
return result.status == 204
def detach_volume(self, volume):
"""
Detaches a volume from a node.
:param volume: Volume to be detached
:type volume: :class:`.StorageVolume`
:rtype: ``bool``
"""
node = volume.extra["relations"]["servers"][0]
result = self._sync_request(
endpoint="objects/servers/{}/storages/{}".format(node["object_uuid"], volume.id),
method="DELETE",
)
return result.status == 204
def ex_storage_rollback(self, volume, snapshot, rollback):
"""
initiate a rollback on your storage
:param volume: storage uuid
:type volume: ``string``
:param snapshot: snapshot uuid
:type snapshot: ``string``
:param rollback: variable
:type rollback: ``bool``
:return: RequestID
:rtype: ``str``
"""
result = self._sync_request(
data={"rollback": rollback},
endpoint="objects/storages/{}/snapshots/" "{}/rollback".format(volume.id, snapshot.id),
method="PATCH",
)
return result
def ex_list_volumes_for_node(self, node):
"""
Return a list of associated volumes for the provided node.
:rtype: ``list`` of :class:`StorageVolume`
"""
volumes = self.list_volumes()
result = []
for volume in volumes:
related_servers = volume.extra.get("relations", {}).get("servers", [])
for server in related_servers:
if server["object_uuid"] == node.id:
result.append(volume)
return result
def ex_list_ips_for_node(self, node):
"""
Return a list of associated IPs for the provided node.
:rype: ``list`` of :class:`GridscaleIp`
"""
ips = self.ex_list_ips()
result = []
for ip in ips:
related_servers = ip.extra.get("relations", {}).get("servers", [])
for server in related_servers:
# TODO: This is not consistent with volumes where key is
# called "object_uuid"
if server["server_uuid"] == node.id:
result.append(ip)
return result
def _to_node(self, data):
extra_keys = ["cores", "power", "memory", "current_price", "relations"]
extra = self._extract_values_to_dict(data=data, keys=extra_keys)
ips = []
for diction in data["relations"]["public_ips"]:
ips.append(diction["ip"])
state = ""
if data["power"] is True:
state = NodeState.RUNNING
else:
state = NodeState.STOPPED
node = Node(
id=data["object_uuid"],
name=data["name"],
state=state,
public_ips=ips,
created_at=parse_date(data["create_time"]),
private_ips=None,
driver=self.connection.driver,
extra=extra,
)
return node
def _to_volume(self, data):
extra_keys = ["create_time", "current_price", "storage_type", "relations"]
extra = self._extract_values_to_dict(data=data, keys=extra_keys)
storage = StorageVolume(
id=data["object_uuid"],
name=data["name"],
size=data["capacity"],
driver=self.connection.driver,
extra=extra,
)
return storage
def _to_volume_snapshot(self, data):
extra_keys = [
"labels",
"status",
"usage_in_minutes",
"location_country",
"current_price",
"parent_uuid",
]
extra = self._extract_values_to_dict(data=data, keys=extra_keys)
volume_snapshot = VolumeSnapshot(
id=data["object_uuid"],
driver=self.connection.driver,
size=data["capacity"],
extra=extra,
created=parse_date(data["create_time"]),
state=StorageVolumeState.AVAILABLE,
name=data["name"],
)
return volume_snapshot
def _to_location(self, data):
location = NodeLocation(
id=data["object_uuid"],
name=data["name"],
country=data["country"],
driver=self.connection.driver,
)
return location
def _to_ip(self, data):
extra_keys = [
"create_time",
"current_price",
"name",
"relations",
"reverse_dns",
"status",
]
extra = self._extract_values_to_dict(data=data, keys=extra_keys)
ip = GridscaleIp(
id=data["object_uuid"],
family=data["family"],
prefix=data["prefix"],
create_time=data["create_time"],
address=data["ip"],
extra=extra,
)
return ip
def _to_network(self, data):
network = GridscaleNetwork(
id=data["object_uuid"],
name=data["name"],
create_time=data["create_time"],
status=data["status"],
relations=data["relations"],
)
return network
def _to_node_image(self, data):
extra_keys = [
"capacity",
"create_time",
"labels",
"ostype",
"location_name",
"private",
"status",
"usage_in_minutes",
"version",
]
extra = self._extract_values_to_dict(data=data, keys=extra_keys)
template = NodeImage(
id=data["object_uuid"],
name=data["name"],
driver=self.connection.driver,
extra=extra,
)
return template
def _to_key(self, data, name=None, sshkey=None):
extra = {"uuid": data["object_uuid"], "labels": data.get("labels", [])}
name = data.get("name", name)
sshkey = data.get("sshkey", sshkey)
key = KeyPair(
name=name,
fingerprint=data["object_uuid"],
public_key=sshkey,
private_key=None,
extra=extra,
driver=self.connection.driver,
)
return key
def _extract_values_to_dict(self, data, keys):
"""
Extract extra values to dict.
:param data: dict to extract values from.
:type data: ``dict``
:param keys: keys to extract
:type keys: ``List``
:return: dictionary containing extra values
:rtype: ``dict``
"""
result = {}
for key in keys:
if key == "memory":
result[key] = data[key] * 1024
else:
result[key] = data[key]
return result
def _get_response_dict(self, raw_response):
"""
Get the actual response dictionary.
:param raw_response: Nested dictionary.
:type raw_response: ``dict``
:return: Not-nested dictionary.
:rtype: ``dict``
"""
return list(raw_response.object.values())[0]
def _get_resource(self, endpoint_suffix, object_uuid):
"""
Get specific uuid specific resource.
:param endpoint_suffix: Endpoint resource e.g. servers/.
:type endpoint_suffix: ``str``
:param object_uuid: Uuid of resource to be pulled.
:type object_uuid: ``str``
:return: Response dictionary.
:rtype: nested ``dict``
"""
data = self._sync_request(endpoint="objects/{}/{}".format(endpoint_suffix, object_uuid))
data = self._get_response_dict(data)
return data