blob: bc31986016f7849f1f173f3d38ad1980e0974c15 [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.
"""
1&1 Cloud Server Compute driver
"""
import json
from time import sleep
from libcloud.utils.py3 import httplib
from libcloud.common.base import JsonResponse, ConnectionKey
from libcloud.common.types import InvalidCredsError
from libcloud.compute.base import (
Node,
NodeSize,
NodeImage,
NodeDriver,
NodeLocation,
NodeAuthSSHKey,
NodeAuthPassword,
)
from libcloud.compute.types import NodeState
from libcloud.compute.providers import Provider
API_HOST = "cloudpanel-api.1and1.com"
API_VERSION = "/v1/"
__all__ = [
"API_HOST",
"API_VERSION",
"OneAndOneResponse",
"OneAndOneConnection",
"OneAndOneNodeDriver",
]
class OneAndOneResponse(JsonResponse):
"""
OneAndOne response parsing.
"""
valid_response_codes = [httplib.OK, httplib.CREATED, httplib.ACCEPTED]
def parse_error(self):
if self.status == httplib.UNAUTHORIZED:
body = self.parse_body()
raise InvalidCredsError(body["message"])
else:
body = self.parse_body()
if "message" in body:
error = "{} (code: {})".format(body["message"], self.status)
else:
error = body
return error
def success(self):
return self.status in self.valid_response_codes
class OneAndOneConnection(ConnectionKey):
"""
Connection class for the 1&1 driver
"""
host = API_HOST
api_prefix = API_VERSION
responseCls = OneAndOneResponse
def encode_data(self, data):
return json.dumps(data)
def add_default_headers(self, headers):
"""
Add headers that are necessary for every request
This method adds ``token`` and ``Content-Type`` to the request.
"""
headers["X-Token"] = self.key
headers["Content-Type"] = "application/json"
return headers
def request(self, action, params=None, data=None, headers=None, method="GET", raw=False):
"""
Some requests will use the href attribute directly.
If this is not the case, then we should formulate the
url based on the action specified.
If we are using a full url, we need to remove the
host and protocol components.
"""
action = self.api_prefix + action.lstrip("/")
return super().request(
action=action,
params=params,
data=data,
headers=headers,
method=method,
raw=raw,
)
class OneAndOneNodeDriver(NodeDriver):
"""
Base OneAndOne node driver.
"""
connectionCls = OneAndOneConnection
name = "1and1"
website = "http://www.1and1.com"
type = Provider.ONEANDONE
NODE_STATE_MAP = {
"POWERING_ON": NodeState.STARTING,
"POWERING_OFF": NodeState.PENDING,
"POWERED_OFF": NodeState.STOPPING,
"POWERED_ON": NodeState.RUNNING,
"REBOOTING": NodeState.REBOOTING,
"CONFIGURING": NodeState.RECONFIGURING,
"REMOVING": NodeState.UNKNOWN,
"DEPLOYING": NodeState.STARTING,
}
"""
Core Functions
"""
def list_sizes(self):
"""
Lists all sizes
:return: A list of all configurable node sizes.
:rtype: ``list`` of :class:`NodeSize`
"""
sizes = []
fixed_instances = self._list_fixed_instances()
for value in fixed_instances:
node_size = self._to_node_size(value)
sizes.append(node_size)
return sizes
def list_locations(self):
"""
Lists all locations
:return: ``list`` of :class:`NodeLocation`
:rtype: ``list``
"""
datacenters = self.ex_list_datacenters()
locations = []
for values in datacenters:
node_size = self._to_location(values)
locations.append(node_size)
return locations
def list_images(self, image_type=None):
"""
:return: ``list`` of :class: `NodeImage`
:rtype: ``list``
"""
response = self.connection.request(action="server_appliances", method="GET")
return self._to_images(response.object, image_type)
def get_image(self, image_id):
response = self.connection.request(action="server_appliances/%s" % image_id, method="GET")
return self._to_image(response.object)
"""
Node functions
"""
def create_node(
self,
name,
image,
ex_fixed_instance_size_id,
location=None,
auth=None,
ex_ip=None,
ex_monitoring_policy_id=None,
ex_firewall_policy_id=None,
ex_loadbalancer_id=None,
ex_description=None,
ex_power_on=None,
):
"""
Creates a node.
:param name: The name of the new node
:type name: `str`
:param ex_fixed_instance_size_id:
Fixed instance size ID from list_sizes
:type ex_fixed_instance_size_id: ``str``
:param location: 1&1 Data center Location
:type location: `NodeLocation`
:param ex_ip: IP address
:type ex_ip: `str`
:param ex_ssh_key: SSH Key
:type ex_ssh_key: `str`
:param password: Password
:type password: `str`
:param ex_monitoring_policy_id:
:type ex_firewall_policy_id: `str`
:param ex_firewall_policy_id:
:type ex_firewall_policy_id: `str`
:param ex_loadbalancer_id:
:type ex_loadbalancer_id: `str`
:param ex_description:
:type ex_description: `str`
:param ex_power_on:
:type ex_power_on: `bool`
:return: Instance of class ``Node``
:rtype: :class:`Node`
"""
body = {
"name": name,
"appliance_id": image.id,
"hardware": {"fixed_instance_size_id": ex_fixed_instance_size_id},
}
if location is not None:
body["datacenter_id"] = location.id
if ex_power_on is not None:
body["power_on"] = ex_power_on
if ex_description is not None:
body["description"] = ex_description
if ex_firewall_policy_id is not None:
body["firewall_policy_id"] = ex_firewall_policy_id
if ex_monitoring_policy_id is not None:
body["monitoring_policy_id"] = ex_monitoring_policy_id
if ex_loadbalancer_id is not None:
body["loadbalancer_id"] = ex_loadbalancer_id
if auth is not None:
if isinstance(auth, NodeAuthPassword):
body["password"] = auth.password
elif isinstance(auth, NodeAuthSSHKey):
body["rsa_key"] = auth.pubkey
if ex_ip is not None:
body["ip_id"] = ex_ip
response = self.connection.request(
action="servers",
data=body,
method="POST",
)
return self._to_node(response.object)
def list_nodes(self):
"""
List all nodes.
:return: ``list`` of :class:`Node`
:rtype: ``list``
"""
response = self.connection.request(action="servers", method="GET")
return self._to_nodes(response.object)
def destroy_node(self, node, ex_keep_ips=False):
"""
Destroys a node.
:param node: The node you wish to destroy.
:type volume: :class:`Node`
:param ex_keep_ips: True to keep all IP addresses assigned to the node
:type ex_keep_ips: : ``bool``
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
self.ex_shutdown_server(node.id)
self._wait_for_state(node.id, "POWERED_OFF")
response = self.connection.request(
action="servers/%s" % node.id,
params={"keep_ips": ex_keep_ips},
method="DELETE",
)
return self._to_node(response.object)
def reboot_node(self, node):
"""
Reboots the node.
:param node: The node you wish to destroy.
:type volume: :class:`Node`
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
shutdown_body = {"action": "REBOOT", "method": "HARDWARE"}
response = self.connection.request(
action="servers/%s/status/action" % node.id,
data=shutdown_body,
method="PUT",
)
return self._to_node(response.object)
"""
Extension functions
"""
def ex_rename_server(self, server_id, name=None, description=None):
"""
Renames the server
:param server_id: ID of the server you want to rename
:param name: New name of the server
:type: ``str``
:param description: New description of the server
:type: ``str``
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
body = {}
if name is not None:
body["name"] = name
if description is not None:
body["description"] = description
response = self.connection.request(action="servers/%s" % server_id, data=body, method="PUT")
return self._to_node(response.object)
def ex_get_server_hardware(self, server_id):
"""
Gets all server hardware
:param server_id: Id of the server
:type: ``str``
:return: Server's hardware
:rtype: ``dict``
"""
response = self.connection.request(action="servers/%s/hardware" % server_id, method="GET")
return response.object
"""
Hardware operations
"""
def ex_modify_server_hardware(
self,
server_id,
fixed_instance_size_id=None,
vcore=None,
cores_per_processor=None,
ram=None,
):
"""
Modifies server's hardware
:param server_id:
:type: ``str``
:param fixed_instance_size_id: Id of the fixed instance size
:type: ``str``
:param vcore: Virtual cores count
:type: ``int``
:param cores_per_processor: Count of cores per procesor
:type: ``int``
:param ram: Amount of ram for the server
:type: ``int``
:return: Instance of class ``Node``
:type: :class: `Node`
"""
body = {}
if fixed_instance_size_id is not None:
body["fixed_instance_size_id"] = fixed_instance_size_id
if vcore is not None:
body["vcore"] = vcore
if cores_per_processor is not None:
body["cores_per_processor"] = cores_per_processor
if ram is not None:
body["ram"] = ram
response = self.connection.request(
action="servers/%s/hardware" % server_id, data=body, method="PUT"
)
return self._to_node(response.object)
"""
HDD operations
"""
def ex_modify_server_hdd(self, server_id, hdd_id=None, size=None):
"""
Modifies server hard disk drives
:param server_id: Id of the server
:type: ``str``
:param hdd_id: Id of the hard disk
:type: ``str``
:param size: Size of the hard disk
:type: ``str``
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
body = {}
if size is not None:
body["size"] = size
response = self.connection.request(
action="servers/{}/hardware/hdds/{}".format(server_id, hdd_id),
data=body,
method="PUT",
)
return self._to_node(response.object)
def ex_add_hdd(self, server_id, size, is_main):
"""
Add a hard disk to the server
:param server_id: Id of the server
:type: ``str``
:param size: Size of the new disk
:type: ``str``
:param is_main: Indicates if the disk is going to be the boot disk
:type: ``boolean``
:return: Instance of class ``Node``
:type: :class: `Node`
"""
body = {"size": size, "is_main": is_main}
response = self.connection.request(
action="servers/%s/hardware/hdds" % server_id, data=body, method="POST"
)
return self._to_node(response.object)
def ex_remove_hdd(self, server_id, hdd_id):
"""
Removes existing hard disk
:param server_id: Id of the server
:type: ``str``
:param hdd_id: Id of the hard disk
:type: ``str``
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
response = self.connection.request(
action="servers/{}/hardware/hdds/{}".format(server_id, hdd_id), method="DELETE"
)
return self._to_node(response.object)
"""
Data center operations
"""
def ex_list_datacenters(self):
"""
Lists all data centers
:return: List of data centers
:rtype: ``dict``
"""
response = self.connection.request(action="datacenters", method="GET")
return response.object
def ex_get_server(self, server_id):
"""
Gets a server
:param server_id: Id of the server to be retrieved
:type: ``str``
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
response = self.connection.request(action="servers/%s" % (server_id), method="GET")
return self._to_node(response.object)
def ex_shutdown_server(self, server_id, method="SOFTWARE"):
"""
Shuts down the server
:param server_id: Id of the server to be shut down
:type: ``str``
:param method: Method of shutting down "SOFTWARE" or "HARDWARE"
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
shutdown_body = {"action": "POWER_OFF", "method": method}
response = self.connection.request(
action="servers/%s/status/action" % (server_id),
data=shutdown_body,
method="PUT",
)
return self._to_node(response.object)
"""
Image operations
"""
def ex_get_server_image(self, server_id):
"""
Gets server image
:param server_id: Id of the server
:type: ``str``
:return: Server image
:rtype: ``dict``
"""
response = self.connection.request(action="servers/%s/image" % server_id, method="GET")
return response.object
def ex_reinstall_server_image(self, server_id, image_id, password=None):
"""
Installs a new image on the server
:param server_id: Id of the server
:type: ``str``
:param image_id: Id of the image (Server Appliance)
:type: ``str``
:param password: New password for the server
:return: Instance of class ``Node``
:rtype: :class: `Node`
"""
body = {
"id": image_id,
}
if password is not None:
body["password"] = password
response = self.connection.request(
action="servers/%s/image" % server_id, data=body, method="PUT"
)
return self._to_node(response.object)
"""
Server IP operations
"""
def ex_list_server_ips(self, server_id):
"""
Gets all server IP objects
:param server_id: Id of the server
:type: ``str``
:return: List of server IP objects
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(action="servers/%s/ips" % server_id, method="GET")
return response.object
def ex_get_server_ip(self, server_id, ip_id):
"""
Get a single server IP object
:param server_id: Id of the server
:type: ``str``
:param ip_id: ID of the IP address
:type: ``str``
:return: IP address object
:rtype: ``dict``
"""
response = self.connection.request(
action="servers/{}/ips/{}".format(server_id, ip_id), method="GET"
)
return response.object
def ex_assign_server_ip(self, server_id, ip_type):
"""
Assigns a new IP address to the server
:param server_id: Id of the server
:type: ``str``
:param ip_type: Type of the IP address [IPV4,IPV6]
:type: ``str``
:return: ``Node`` instance
:rtype: ``Node``
"""
body = {"type": ip_type}
response = self.connection.request(
action="servers/%s/ips" % server_id, data=body, method="POST"
)
return self._to_node(response.object)
def ex_remove_server_ip(self, server_id, ip_id, keep_ip=None):
"""
Removes an IP address from the server
:param server_id: Id of the server
:type: ``str``
:param ip_id: ID of the IP address
:type: ``str``
:param keep_ip: Indicates whether IP address will be removed from
the Cloud Panel
:type: ``boolean``
:return: ``Node`` instance
:rtype: ``Node``
"""
body = {}
if keep_ip is not None:
body["keep_ip"] = keep_ip
response = self.connection.request(
action="servers/{}/ips/{}".format(server_id, ip_id), data=body, method="DELETE"
)
return self._to_node(response.object)
def ex_get_server_firewall_policies(self, server_id, ip_id):
"""
Gets a firewall policy of attached to the server's IP
:param server_id: Id of the server
:type: ``str``
:param ip_id: ID of the IP address
:type: ``str``
:return: IP address object
:rtype: ``dict``
"""
response = self.connection.request(
action="/servers/{}/ips/{}/firewall_policy".format(server_id, ip_id),
method="GET",
)
return response.object
def ex_add_server_firewall_policy(self, server_id, ip_id, firewall_id):
"""
Adds a firewall policy to the server's IP address
:param server_id: Id of the server
:type: ``str``
:param ip_id: ID of the IP address
:type: ``str``
:param firewall_id: ID of the firewall policy
:type: ``str``
:return: ``Node`` instance
:rtype: ``Node``
"""
body = {"id": firewall_id}
response = self.connection.request(
action="/servers/{}/ips/{}/firewall_policy".format(server_id, ip_id),
data=body,
method="POST",
)
return self._to_node(response.object)
"""
Firewall Policy operations
"""
def ex_create_firewall_policy(self, name, rules, description=None):
"""
Creates a firewall Policy.
:param name:
:param description:
:param rules:
:rtype: `dict`
:return: `dict` firewall policy
"""
body = {"name": name}
if description is not None:
body["description"] = description
if len(rules) == 0:
raise ValueError("At least one firewall rule is required.")
else:
body["rules"] = rules
response = self.connection.request(
action="firewall_policies",
data=body,
method="POST",
)
return response.object
def ex_list_firewall_policies(self):
""" "
List firewall policies
:return: 'dict'
"""
response = self.connection.request(action="firewall_policies", method="GET")
return response.object
def ex_get_firewall_policy(self, fw_id):
"""
Gets firewall policy
:param fw_id: ID of the firewall policy
:return: 'dict'
"""
response = self.connection.request(action="firewall_policy/%s" % fw_id, method="GET")
return response.object
def ex_delete_firewall_policy(self, fw_id):
"""
Deletes firewall policy
:param fw_id: ID of the Firewall
:return: 'dict'
"""
response = self.connection.request(action="firewall_policy/%s" % fw_id, method="DELETE")
return response.object
"""
Shared storage operations
"""
def ex_list_shared_storages(self):
"""
List of shared storages
:return: 'dict'
"""
response = self.connection.request(action="shared_storages", method="GET")
return response.object
def ex_get_shared_storage(self, storage_id):
"""
Gets a shared storage
:return: 'dict'
"""
response = self.connection.request(action="shared_storages/%s" % (storage_id), method="GET")
return response.object
def ex_create_shared_storage(self, name, size, datacenter_id=None, description=None):
"""
Creates a shared storage
:param name: Name of the storage
:param size: Size of the storage
:param datacenter_id: datacenter where storage should be created
:param description: description ot the storage
:return: 'dict'
"""
body = {"name": name, "size": size, "datacenter_id": datacenter_id}
if description is not None:
body["description"] = description
response = self.connection.request(action="shared_storages", data=body, method="POST")
return response.object
def ex_delete_shared_storage(self, storage_id):
"""
Removes a shared storage
:param storage_id: Id of the shared storage
:type: ``str``
:return: Instnace of shared storage
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(
action="shared_storages/%s" % storage_id, method="DELETE"
)
return response.object
def ex_attach_server_to_shared_storage(self, storage_id, server_id, rights):
"""
Attaches a single server to a shared storage
:param storage_id: Id of the shared storage
:param server_id: Id of the server to be attached to the shared storage
:param rights:
:return:
:rtype: 'dict'
"""
body = {"severs": [{"id": server_id, "rights": rights}]}
response = self.connection.request(
action="shared_storages/%s/servers" % storage_id, data=body, method="POST"
)
return response.object
def ex_get_shared_storage_server(self, storage_id, server_id):
"""
Gets a shared storage's server
:param storage_id:
:param server_id:
:return:
"""
response = self.connection.request(
action="shared_storages/{}/servers/{}".format(storage_id, server_id),
)
return response.object
def ex_detach_server_from_shared_storage(self, storage_id, server_id):
"""
Detaches a server from shared storage
:param storage_id: Id of the shared storage
:type: ``str``
:param server_id: Id of the server
:type: ``str``
:return: Instance of shared storage
:rtype: ``dict``
"""
response = self.connection.request(
action="shared_storages/{}/servers/{}".format(storage_id, server_id),
method="DELETE",
)
return response.object
"""
Load Balancers operations
"""
def ex_create_load_balancer(
self,
name,
method,
rules,
persistence=None,
persistence_time=None,
health_check_test=None,
health_check_interval=None,
health_check_path=None,
health_check_parser=None,
datacenter_id=None,
description=None,
):
"""
:param name: Name of the load balancer
:param method: Load balancer method
:param rules: Load balancer rules
:type rules: ``list`` of ``dict``
:param persistence: Indictes if persistance is set
:type persistence: ``boolean``
:param persistence_time: Persistance time
:type persistence_time: ``int``
:param health_check_test: Type of test
:type health_check_test:``str``
:param health_check_interval: Interval of the check
:param health_check_path: Path
:type health_check_path: ``str``
:param health_check_parser: Parser
:type health_check_parser:``str``
:param datacenter_id: Data center id
:type datacenter_id:``str``
:param description: Description of load balancer
:type description:``str``
:return: ``dict``
"""
body = {
"name": name,
"method": method,
}
body["rules"] = []
body["rules"] = rules
if persistence is not None:
body["persistence"] = persistence
if persistence_time is not None:
body["persistence_time"] = persistence_time
if health_check_test is not None:
body["health_check_test"] = health_check_test
if health_check_interval is not None:
body["health_check_interval"] = health_check_interval
if health_check_path is not None:
body["health_check_path"] = health_check_path
if health_check_parser is not None:
body["health_check_parser"] = health_check_parser
if datacenter_id is not None:
body["datacenter_id"] = datacenter_id
if description is not None:
body["description"] = description
response = self.connection.request(action="load_balancers", data=body, method="POST")
return response.object
def ex_update_load_balancer(
self,
lb_id,
name=None,
description=None,
health_check_test=None,
health_check_interval=None,
persistence=None,
persistence_time=None,
method=None,
):
body = {}
if name is not None:
body["name"] = name
if description is not None:
body["description"] = description
if health_check_test is not None:
body["health_check_test"] = health_check_test
if health_check_interval is not None:
body["health_check_interval"] = health_check_interval
if persistence is not None:
body["persistence"] = persistence
if persistence_time is not None:
body["persistence_time"] = persistence_time
if method is not None:
body["method"] = method
response = self.connection.request(
action="load_balancers/%s" % lb_id, data=body, method="PUT"
)
return response.object
def ex_add_servers_to_load_balancer(self, lb_id, server_ips=[]):
"""
Adds server's IP address to load balancer
:param lb_id: Load balancer ID
:type: ``str``
:param server_ips: Array of server IP IDs
:type: ``list`` of ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
body = {
"server_ips": server_ips,
}
response = self.connection.request(
action="load_balancers/%s/server_ips" % lb_id, data=body, method="POST"
)
return response.object
def ex_remove_server_from_load_balancer(self, lb_id, server_ip):
"""
Removes server's IP from load balancer
:param lb_id: Load balancer ID
:type: ``str``
:param server_ip: ID of the server IP
:type: ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
response = self.connection.request(
action="/load_balancers/{}/server_ips/{}".format(lb_id, server_ip),
method="DELETE",
)
return response.object
def ex_add_load_balancer_rule(self, lb_id, protocol, port_balancer, port_server, source=None):
"""
Adds a rule to load balancer
:param lb_id: Load balancer ID
:rtype: ``str``
:param protocol: Load balancer protocol
:rtype: ``str``
:param port_balancer: Port to be balananced
:rtype: ``int``
:param port_server: Server port
:rtype: ``int``
:param source: Source IP address
:rtype: ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
body = {
"rules": [
{
"protocol": protocol,
"port_balancer": port_balancer,
"port_server": port_server,
}
]
}
if source is not None:
body["rules"][0]["source"] = source
response = self.connection.request(
action="/load_balancers/%s/rules" % lb_id, data=body, method="POST"
)
return response.object
def ex_remove_load_balancer_rule(self, lb_id, rule_id):
"""
Removes load balancer rule
:param lb_id: Load balancer ID
:rtype: ``str``
:param rule_id: Rule ID
:rtype: ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
response = self.connection.request(
action="/load_balancers/{}/rules/{}".format(lb_id, rule_id), method="DELETE"
)
return response.object
def ex_list_load_balancers(self):
"""
Lists all load balancers
:return: List of load balancers
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(action="load_balancers", method="GET")
return response.object
def ex_get_load_balancer(self, lb_id):
"""
Gets a single load balancer
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
response = self.connection.request(action="load_balancers/%s" % lb_id, method="GET")
return response.object
def ex_list_load_balancer_server_ips(self, lb_id):
"""
List balanced server IP addresses
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:return: Array of IP address IDs
:rtype: ``dict``
"""
response = self.connection.request(
action="load_balancers/%s/server_ips" % lb_id, method="GET"
)
return response.object
def ex_get_load_balancer_server_ip(self, lb_id, server_ip):
"""
Gets load balanced server id
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:param server_ip: ID of the server IP
:type server_ip: ``str``
:return: Server IP
:rtype: ``dict``
"""
response = self.connection.request(
action="load_balancers/{}/server_ips/{}".format(lb_id, server_ip), method="GET"
)
return response.object
def ex_list_load_balancer_rules(self, lb_id):
"""
Lists loadbalancer rules
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:return: Lists of rules
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(action="load_balancers/%s/rules" % lb_id, method="GET")
return response.object
def ex_get_load_balancer_rule(self, lb_id, rule_id):
"""
Get a load balancer rule
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:param rule_id: Rule ID
:type rule_id: ``str``
:return: A load balancer rule
:rtype: ``dict``
"""
response = self.connection.request(
action="load_balancers/{}/rules/{}".format(lb_id, rule_id), method="GET"
)
return response.object
def ex_delete_load_balancer(self, lb_id):
"""
Deletes a load balancer rule
:param lb_id: ID of the load balancer
:type lb_id: ``str``
:param rule_id: Rule ID
:type rule_id: ``str``
:return: Instance of load balancer
:rtype: ``dict``
"""
response = self.connection.request(action="load_balancers/%s" % lb_id, method="DELETE")
return response.object
"""
Public IP operations
"""
def ex_list_public_ips(self):
"""
Lists all public IP addresses
:return: Array of public addresses
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(action="public_ips", method="GET")
return response.object
def ex_create_public_ip(self, type, reverse_dns=None, datacenter_id=None):
"""
Creates a public IP
:param type: Type of IP (IPV4 or IPV6)
:type type: ``str``
:param reverse_dns: Reverse DNS
:type reverse_dns: ``str``
:param datacenter_id: Datacenter ID where IP address will be crated
:type datacenter_id: ``str``
:return: Instance of Public IP
:rtype: ``dict``
"""
body = {"type": type}
if reverse_dns is not None:
body["reverse_dns"] = reverse_dns
if datacenter_id is not None:
body["datacenter_id"] = datacenter_id
response = self.connection.request(action="public_ips", data=body, method="POST")
return response.object
def ex_get_public_ip(self, ip_id):
"""
Gets a Public IP
:param ip_id: ID of the IP
:type ip_id: ``str``
:return: Instance of Public IP
:rtype: ``dict``
"""
response = self.connection.request(action="public_ips/%s" % ip_id, method="GET")
return response.object
def ex_delete_public_ip(self, ip_id):
"""
Deletes a public IP
:param ip_id: ID of public IP
:type ip_id: ``str``
:return: Instance of IP Address
:rtype: ``dict``
"""
response = self.connection.request(action="public_ips/%s" % ip_id, method="DELETE")
return response
def ex_update_public_ip(self, ip_id, reverse_dns):
"""
Updates a Public IP
:param ip_id: ID of public IP
:type ip_id: ``str``
:param reverse_dns: Reverse DNS
:type reverse_dns: ``str``
:return: Instance of Public IP
:rtype: ``dict``
"""
body = {"reverse_dns": reverse_dns}
response = self.connection.request(
action="public_ips/%s" % ip_id, data=body, method="DELETE"
)
return response.object
"""
Private Network Operations
"""
def ex_list_private_networks(self):
"""
Lists all private networks
:return: List of private networks
:rtype: ``dict``
"""
response = self.connection.request(action="private_networks", method="GET")
return response.object
def ex_create_private_network(
self,
name,
description=None,
datacenter_id=None,
network_address=None,
subnet_mask=None,
):
"""
Creates a private network
:param name: Name of the private network
:type name: ``str``
:param description: Description of the private network
:type description: ``str``
:param datacenter_id: ID of the data center for the private network
:type datacenter_id: ``str``
:param network_address: Network address of the private network
:type network_address: ``str``
:param subnet_mask: Subnet mask of the private network
:type subnet_mask: ``str``
:return: Newly created private network
:rtype: ``dict``
"""
body = {"name": name}
if description is not None:
body["description"] = description
if datacenter_id is not None:
body["datacenter_id"] = datacenter_id
if network_address is not None:
body["network_address"] = network_address
if subnet_mask is not None:
body["subnet_maks"] = subnet_mask
response = self.connection.request(action="private_networks", data=body, method="POST")
return response.object
def ex_delete_private_network(self, network_id):
"""
Deletes a private network
:param network_id: Id of the private network
:type network_id: ``str``
:return: Instance of the private network being deleted
:rtype: ``dict``
"""
response = self.connection.request(action="private_networks" % network_id, method="DELETE")
return response.object
def ex_update_private_network(
self,
network_id,
name=None,
description=None,
datacenter_id=None,
network_address=None,
subnet_mask=None,
):
"""
Updates a private network
:param name: Name of the private network
:type name: ``str``
:param description: Description of the private network
:type description: ``str``
:param datacenter_id: ID of the data center for the private network
:type datacenter_id: ``str``
:param network_address: Network address of the private network
:type network_address: ``str``
:param subnet_mask: Subnet mask of the private network
:type subnet_mask: ``str``
:return: Instance of private network
:rtype: ``dict``
"""
body = {}
if name is not None:
body["name"] = name
if description is not None:
body["description"] = description
if datacenter_id is not None:
body["datacenter_id"] = datacenter_id
if network_address is not None:
body["network_address"] = network_address
if subnet_mask is not None:
body["subnet_maks"] = subnet_mask
response = self.connection.request(action="private_networks/%s", data=body, method="PUT")
return response.object
def ex_list_private_network_servers(self, network_id):
"""
Lists all private network servers
:param network_id: Private network ID
:type network_id: ``str``
:return: List of private network servers
:rtype: ``dict``
"""
response = self.connection.request(
action="/private_networks/%s/servers" % network_id, method="GET"
)
return response.object
def ex_add_private_network_server(self, network_id, server_ids):
"""
Add servers to private network
:param network_id: Private Network ID
:type network_id: ``str``
:param server_ids: List of server IDs
:type server_ids: ``list`` of ``str``
:return: List of attached servers
:rtype: ``dict``
"""
body = {"servers": server_ids}
response = self.connection.request(
action="/private_networks/%s/servers" % network_id, data=body, method="POST"
)
return response.object
def ex_remove_server_from_private_network(self, network_id, server_id):
"""
Removes a server from the private network
:param network_id: Private Network ID
:type network_id: ``str``
:param server_id: Id of the server
:type server_id: ``str``
:return: Instance of the private network
:rtype: ``dict``
"""
response = self.connection.request(
action="/private_networks/{}/servers/{}".format(network_id, server_id),
method="POST",
)
return response.object
"""
Monitoring policy operations
"""
def ex_list_monitoring_policies(self):
"""
Lists all monitoring policies
:return: List of monitoring policies
:rtype: ``dict``
"""
response = self.connection.request(action="monitoring_policies", method="GET")
return response.object
def ex_create_monitoring_policy(
self,
name,
thresholds,
ports,
processes,
description=None,
email=None,
agent=None,
):
"""
Creates a monitoring policy
:param name: Name for the monitoring policy
:type name: ``str``
:param thresholds: Thresholds for the monitoring policy
:type thresholds: ``dict``
:param ports: Monitoring policies for ports
:type ports: ``list`` of ``dict``
:param processes: Processes to be monitored
:type processes: ``list`` of ``dict``
:param description: Description for the monitoring policy
:type description: ``str``
:param email: Email for notifications
:type email: ``str``
:param agent: Indicates if agent application will be installed
:type agent: ``boolean``
:return: Newly created instance of monitofing policy
:rtype: ``dict``
"""
body = {
"name": name,
"thresholds": thresholds,
"ports": ports,
"processes": processes,
}
if description is not None:
body["description"] = description
if email is not None:
body["email"] = email
if agent is not None:
body["agent"] = agent
response = self.connection.request(action="monitoring_policies", data=body, method="POST")
return response.object
def ex_delete_monitoring_policy(self, policy_id):
"""
Deletes a monitoring policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:return: Instance of the monitoring policy being deleted
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies" % policy_id, method="DELETE"
)
return response.object
def ex_update_monitoring_policy(
self, policy_id, email, thresholds, name=None, description=None
):
"""
Updates monitoring policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param email: Email to send notifications to
:type email: ``str``
:param thresholds: Thresholds for the monitoring policy
:type thresholds: ``dict``
:param name: Name of the monitoring policy
:type name: ``str``
:param description: Description of the monitoring policy
:type description: ``str``
:return: Instance of the monitoring policy being deleted
:rtype: ``dict``
"""
body = {}
if name is not None:
body["name"] = name
if description is not None:
body["description"] = description
if thresholds is not None:
body["thresholds"] = thresholds
if email is not None:
body["email"] = email
response = self.connection.request(
action="monitoring_policies/%s" % policy_id, data=body, method="PUT"
)
return response.object
def ex_get_monitoring_policy(self, policy_id):
"""
Fetches a monitoring policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/%s" % policy_id, method="GET"
)
return response.object
def ex_get_monitoring_policy_ports(self, policy_id):
"""
Fetches monitoring policy ports
:param policy_id: Id of the monitoring policy
:type policy_id:
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/%s/ports" % policy_id, method="GET"
)
return response.object
def ex_get_monitoring_policy_port(self, policy_id, port_id):
"""
Fetches monitoring policy port
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param port_id: Id of the port
:type port_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/{}/ports/{}".format(policy_id, port_id),
method="GET",
)
return response.object
def ex_remove_monitoring_policy_port(self, policy_id, port_id):
"""
Removes monitoring policy port
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param port_id: Id of the port
:type port_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/{}/ports/{}".format(policy_id, port_id),
method="DELETE",
)
return response.object
def ex_add_monitoring_policy_ports(self, policy_id, ports):
"""
Add monitoring policy ports
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param ports: List of ports
:type ports: ``dict``
[
{
'protocol':'TCP',
'port':'80',
'alert_if':'RESPONDING',
'email_notification':true
}
]
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
body = {"ports": ports}
response = self.connection.request(
action="monitoring_policies/%s/ports" % policy_id, data=body, method="POST"
)
return response.object
def ex_get_monitoring_policy_processes(self, policy_id):
"""
Fetches monitoring policy processes
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/%s/processes" % policy_id, method="GET"
)
return response.object
def ex_get_monitoring_policy_process(self, policy_id, process_id):
"""
Fetches monitoring policy process
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param process_id: Id of the process
:type process_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/{}/processes/{}".format(policy_id, process_id),
method="GET",
)
return response.object
def ex_remove_monitoring_policy_process(self, policy_id, process_id):
"""
Removes monitoring policy process
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param process_id: Id of the process
:type process_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/{}/processes/{}".format(policy_id, process_id),
method="DELETE",
)
return response.object
def ex_add_monitoring_policy_processes(self, policy_id, processes):
"""
Add monitoring policy processes
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param processes: List of processes
:type processes: ``list`` of ``dict``
[
{
'process': 'taskmmgr',
'alert_if': 'RUNNING',
'email_notification': true
}
]
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
body = {"processes": processes}
response = self.connection.request(
action="monitoring_policies/%s/processes" % policy_id,
data=body,
method="POST",
)
return response.object
def ex_list_monitoring_policy_servers(self, policy_id):
"""
List all servers that are being monitoried by the policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:return: List of servers being monitored
:rtype: ``list`` of ``dict``
"""
response = self.connection.request(
action="monitoring_policies/%s/servers" % policy_id, method="GET"
)
return response.object
def ex_add_servers_to_monitoring_policy(self, policy_id, servers):
"""
Adds servers to monitoring policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param servers: List of server ID
:type servers: ``list`` of ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
body = {"servers": servers}
response = self.connection.request(
action="monitoring_policies/%s/servers" % policy_id,
data=body,
method="POST",
)
return response.object
def ex_remove_server_from_monitoring_policy(self, policy_id, server_id):
"""
Removes a server from monitoring policy
:param policy_id: Id of the monitoring policy
:type policy_id: ``str``
:param server_id: Id of the server
:type server_id: ``str``
:return: Instance of a monitoring policy
:rtype: ``dict``
"""
response = self.connection.request(
action="monitoring_policies/{}/servers/{}".format(policy_id, server_id),
method="DELETE",
)
return response.object
"""
Private Functions
"""
def _to_images(self, object, image_type=None):
if image_type is not None:
images = [image for image in object if image["type"] == image_type]
else:
images = [image for image in object]
return [self._to_image(image) for image in images]
def _to_image(self, data):
extra = {
"os_family": data["os_family"],
"os": data["os"],
"os_version": data["os_version"],
"os_architecture": data["os_architecture"],
"os_image_type": data["os_image_type"],
"min_hdd_size": data["min_hdd_size"],
"available_datacenters": data["available_datacenters"],
"licenses": data["licenses"],
"version": data["version"],
"categories": data["categories"],
}
return NodeImage(id=data["id"], name=data["name"], driver=self, extra=extra)
def _to_node_size(self, data):
return NodeSize(
id=data["id"],
name=data["name"],
ram=data["hardware"]["ram"],
disk=data["hardware"]["hdds"][0]["size"],
bandwidth=None,
price=None,
driver=self.connection.driver,
extra={
"vcores": data["hardware"]["vcore"],
"cores_per_processor": data["hardware"]["cores_per_processor"],
},
)
def _to_location(self, location):
return NodeLocation(
id=location["id"],
name=location["country_code"],
country=location["location"],
driver=self.connection.driver,
)
def _to_nodes(self, servers):
return [self._to_node(server) for server in servers]
def _to_node(self, server):
extra = {}
extra["datacenter"] = server["datacenter"]
if "description" in server:
extra["description"] = server["description"]
if "status" in server:
extra["status"] = server["status"]
if "image" in server:
extra["image"] = server["image"]
if "hardware" in server:
extra["hardware"] = server["hardware"]
if "dvd" in server:
extra["dvd"] = server["dvd"]
if "snapshot" in server:
extra["snapshot"] = server["snapshot"]
if "ips" in server:
extra["ips"] = server["ips"]
if "alerts" in server:
extra["alerts"] = server["alerts"]
if "monitoring_policy" in server:
extra["monitoring_policy"] = server["monitoring_policy"]
if "private_networks" in server:
extra["private_networks"] = server["private_networks"]
ips = []
if server["ips"] is not None:
for ip in server["ips"]:
ips.append(ip["ip"])
state = self.NODE_STATE_MAP.get(server["status"]["state"])
return Node(
id=server["id"],
state=state,
name=server["name"],
driver=self.connection.driver,
public_ips=ips,
private_ips=None,
extra=extra,
)
def _wait_for_state(self, server_id, state, retries=50):
for i in (0, retries):
server = self.ex_get_server(server_id)
if server.extra["status"]["state"] == state:
return
sleep(5)
if i == retries:
raise Exception("Retries count reached")
def _list_fixed_instances(self):
response = self.connection.request(action="/servers/fixed_instance_sizes", method="GET")
return response.object