blob: 8e09846460e4a1f47f760c53c81fb8b3249aa132 [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.
"""
Dimension Data Common Components
"""
from base64 import b64encode
from time import sleep
# TODO: use disutils.version when Travis CI fixed the pylint issue with version
# from distutils.version import LooseVersion
from libcloud.utils.py3 import httplib
from libcloud.utils.py3 import b
from libcloud.common.base import ConnectionUserAndKey, XmlResponse, RawResponse
from libcloud.compute.base import Node
from libcloud.utils.py3 import basestring
from libcloud.utils.xml import findtext
from libcloud.compute.types import LibcloudError, InvalidCredsError
# Roadmap / TODO:
#
# 1.0 - Copied from OpSource API, named provider details.
# setup a few variables to represent all of the DimensionData cloud namespaces
NAMESPACE_BASE = "http://oec.api.opsource.net/schemas"
ORGANIZATION_NS = NAMESPACE_BASE + "/organization"
SERVER_NS = NAMESPACE_BASE + "/server"
NETWORK_NS = NAMESPACE_BASE + "/network"
DIRECTORY_NS = NAMESPACE_BASE + "/directory"
GENERAL_NS = NAMESPACE_BASE + "/general"
BACKUP_NS = NAMESPACE_BASE + "/backup"
# API 2.0 Namespaces and URNs
TYPES_URN = "urn:didata.com:api:cloud:types"
# API end-points
API_ENDPOINTS = {
"dd-na": {
"name": "North America (NA)",
"host": "api-na.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-eu": {
"name": "Europe (EU)",
"host": "api-eu.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-au": {
"name": "Australia (AU)",
"host": "api-au.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-au-gov": {
"name": "Australia Canberra ACT (AU)",
"host": "api-canberra.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-af": {
"name": "Africa (AF)",
"host": "api-mea.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-ap": {
"name": "Asia Pacific (AP)",
"host": "api-ap.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-latam": {
"name": "South America (LATAM)",
"host": "api-latam.dimensiondata.com",
"vendor": "DimensionData",
},
"dd-canada": {
"name": "Canada (CA)",
"host": "api-canada.dimensiondata.com",
"vendor": "DimensionData",
},
"is-na": {
"name": "North America (NA)",
"host": "usapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-eu": {
"name": "Europe (EU)",
"host": "euapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-au": {
"name": "Australia (AU)",
"host": "auapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-af": {
"name": "Africa (AF)",
"host": "meaapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-ap": {
"name": "Asia Pacific (AP)",
"host": "apapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-latam": {
"name": "South America (LATAM)",
"host": "latamapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"is-canada": {
"name": "Canada (CA)",
"host": "canadaapi.cloud.is.co.za",
"vendor": "InternetSolutions",
},
"ntta-na": {
"name": "North America (NA)",
"host": "cloudapi.nttamerica.com",
"vendor": "NTTNorthAmerica",
},
"ntta-eu": {
"name": "Europe (EU)",
"host": "eucloudapi.nttamerica.com",
"vendor": "NTTNorthAmerica",
},
"ntta-au": {
"name": "Australia (AU)",
"host": "aucloudapi.nttamerica.com",
"vendor": "NTTNorthAmerica",
},
"ntta-af": {
"name": "Africa (AF)",
"host": "sacloudapi.nttamerica.com",
"vendor": "NTTNorthAmerica",
},
"ntta-ap": {
"name": "Asia Pacific (AP)",
"host": "hkcloudapi.nttamerica.com",
"vendor": "NTTNorthAmerica",
},
"cisco-na": {
"name": "North America (NA)",
"host": "iaas-api-na.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-eu": {
"name": "Europe (EU)",
"host": "iaas-api-eu.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-au": {
"name": "Australia (AU)",
"host": "iaas-api-au.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-af": {
"name": "Africa (AF)",
"host": "iaas-api-mea.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-ap": {
"name": "Asia Pacific (AP)",
"host": "iaas-api-ap.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-latam": {
"name": "South America (LATAM)",
"host": "iaas-api-sa.cisco-ccs.com",
"vendor": "Cisco",
},
"cisco-canada": {
"name": "Canada (CA)",
"host": "iaas-api-ca.cisco-ccs.com",
"vendor": "Cisco",
},
"med1-il": {
"name": "Israel (IL)",
"host": "api.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-na": {
"name": "North America (NA)",
"host": "api-na.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-eu": {
"name": "Europe (EU)",
"host": "api-eu.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-au": {
"name": "Australia (AU)",
"host": "api-au.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-af": {
"name": "Africa (AF)",
"host": "api-af.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-ap": {
"name": "Asia Pacific (AP)",
"host": "api-ap.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-latam": {
"name": "South America (LATAM)",
"host": "api-sa.cloud.med-1.com",
"vendor": "Med-1",
},
"med1-canada": {
"name": "Canada (CA)",
"host": "api-ca.cloud.med-1.com",
"vendor": "Med-1",
},
"indosat-id": {
"name": "Indonesia (ID)",
"host": "iaas-api.indosat.com",
"vendor": "Indosat",
},
"indosat-na": {
"name": "North America (NA)",
"host": "iaas-usapi.indosat.com",
"vendor": "Indosat",
},
"indosat-eu": {
"name": "Europe (EU)",
"host": "iaas-euapi.indosat.com",
"vendor": "Indosat",
},
"indosat-au": {
"name": "Australia (AU)",
"host": "iaas-auapi.indosat.com",
"vendor": "Indosat",
},
"indosat-af": {
"name": "Africa (AF)",
"host": "iaas-afapi.indosat.com",
"vendor": "Indosat",
},
"bsnl-in": {"name": "India (IN)", "host": "api.bsnlcloud.com", "vendor": "BSNL"},
"bsnl-na": {
"name": "North America (NA)",
"host": "usapi.bsnlcloud.com",
"vendor": "BSNL",
},
"bsnl-eu": {"name": "Europe (EU)", "host": "euapi.bsnlcloud.com", "vendor": "BSNL"},
"bsnl-au": {
"name": "Australia (AU)",
"host": "auapi.bsnlcloud.com",
"vendor": "BSNL",
},
"bsnl-af": {"name": "Africa (AF)", "host": "afapi.bsnlcloud.com", "vendor": "BSNL"},
}
# Default API end-point for the base connection class.
DEFAULT_REGION = "dd-na"
BAD_CODE_XML_ELEMENTS = (
("responseCode", SERVER_NS),
("responseCode", TYPES_URN),
("result", GENERAL_NS),
)
BAD_MESSAGE_XML_ELEMENTS = (
("message", SERVER_NS),
("message", TYPES_URN),
("resultDetail", GENERAL_NS),
)
def dd_object_to_id(obj, obj_type, id_value="id"):
"""
Takes in a DD object or string and prints out it's id
This is a helper method, as many of our functions can take either an object
or a string, and we need an easy way of converting them
:param obj: The object to get the id for
:type obj: ``object``
:param func: The function to call, e.g. ex_get_vlan. Note: This
function needs to return an object which has ``status``
attribute.
:type func: ``function``
:rtype: ``str``
"""
if isinstance(obj, obj_type):
return getattr(obj, id_value)
elif isinstance(obj, (basestring)):
return obj
else:
raise TypeError(
"Invalid type %s looking for basestring or %s"
% (type(obj).__name__, obj_type.__name__)
)
# TODO: use disutils.version when Travis CI fixed the pylint issue with version
# This is a temporary workaround.
def LooseVersion(version):
return float(version)
class NetworkDomainServicePlan(object):
ESSENTIALS = "ESSENTIALS"
ADVANCED = "ADVANCED"
class DimensionDataRawResponse(RawResponse):
pass
class DimensionDataResponse(XmlResponse):
def parse_error(self):
if self.status == httplib.UNAUTHORIZED:
raise InvalidCredsError(self.body)
elif self.status == httplib.FORBIDDEN:
raise InvalidCredsError(self.body)
body = self.parse_body()
if self.status == httplib.BAD_REQUEST:
for response_code in BAD_CODE_XML_ELEMENTS:
code = findtext(body, response_code[0], response_code[1])
if code is not None:
break
for message in BAD_MESSAGE_XML_ELEMENTS:
message = findtext(body, message[0], message[1])
if message is not None:
break
raise DimensionDataAPIException(
code=code, msg=message, driver=self.connection.driver
)
if self.status is not httplib.OK:
raise DimensionDataAPIException(
code=self.status, msg=body, driver=self.connection.driver
)
return self.body
class DimensionDataAPIException(LibcloudError):
def __init__(self, code, msg, driver):
self.code = code
self.msg = msg
self.driver = driver
def __str__(self):
return "%s: %s" % (self.code, self.msg)
def __repr__(self):
return "<DimensionDataAPIException: code='%s', msg='%s'>" % (
self.code,
self.msg,
)
class DimensionDataConnection(ConnectionUserAndKey):
"""
Connection class for the DimensionData driver
"""
api_path_version_1 = "/oec"
api_path_version_2 = "/caas"
api_version_1 = 0.9
# Earliest version supported
oldest_api_version = "2.2"
# Latest version supported
latest_api_version = "2.4"
# Default api version
active_api_version = "2.4"
_orgId = None
responseCls = DimensionDataResponse
rawResponseCls = DimensionDataRawResponse
allow_insecure = False
def __init__(
self,
user_id,
key,
secure=True,
host=None,
port=None,
url=None,
timeout=None,
proxy_url=None,
api_version=None,
**conn_kwargs,
):
super(DimensionDataConnection, self).__init__(
user_id=user_id,
key=key,
secure=secure,
host=host,
port=port,
url=url,
timeout=timeout,
proxy_url=proxy_url,
)
if conn_kwargs["region"]:
self.host = conn_kwargs["region"]["host"]
if api_version:
if LooseVersion(api_version) < LooseVersion(self.oldest_api_version):
msg = (
"API Version specified is too old. No longer "
"supported. Please upgrade to the latest version {}".format(
self.active_api_version
)
)
raise DimensionDataAPIException(code=None, msg=msg, driver=self.driver)
elif LooseVersion(api_version) > LooseVersion(self.latest_api_version):
msg = (
"Unsupported API Version. The version specified is "
"not release yet. Please use the latest supported "
"version {}".format(self.active_api_version)
)
raise DimensionDataAPIException(code=None, msg=msg, driver=self.driver)
else:
# Overwrite default version using the version user specified
self.active_api_version = api_version
def add_default_headers(self, headers):
headers["Authorization"] = "Basic %s" % b64encode(
b("%s:%s" % (self.user_id, self.key))
).decode("utf-8")
headers["Content-Type"] = "application/xml"
return headers
def request_api_1(self, action, params=None, data="", headers=None, method="GET"):
action = "%s/%s/%s" % (self.api_path_version_1, self.api_version_1, action)
return super(DimensionDataConnection, self).request(
action=action, params=params, data=data, method=method, headers=headers
)
def request_api_2(
self, path, action, params=None, data="", headers=None, method="GET"
):
action = "%s/%s/%s/%s" % (
self.api_path_version_2,
self.active_api_version,
path,
action,
)
return super(DimensionDataConnection, self).request(
action=action, params=params, data=data, method=method, headers=headers
)
def raw_request_with_orgId_api_1(
self, action, params=None, data="", headers=None, method="GET"
):
action = "%s/%s" % (self.get_resource_path_api_1(), action)
return super(DimensionDataConnection, self).request(
action=action,
params=params,
data=data,
method=method,
headers=headers,
raw=True,
)
def request_with_orgId_api_1(
self, action, params=None, data="", headers=None, method="GET"
):
action = "%s/%s" % (self.get_resource_path_api_1(), action)
return super(DimensionDataConnection, self).request(
action=action, params=params, data=data, method=method, headers=headers
)
def request_with_orgId_api_2(
self, action, params=None, data="", headers=None, method="GET"
):
action = "%s/%s" % (self.get_resource_path_api_2(), action)
return super(DimensionDataConnection, self).request(
action=action, params=params, data=data, method=method, headers=headers
)
def paginated_request_with_orgId_api_2(
self, action, params=None, data="", headers=None, method="GET", page_size=250
):
"""
A paginated request to the MCP2.0 API
This essentially calls out to request_with_orgId_api_2 for each page
and yields the response to make a generator
This generator can be looped through to grab all the pages.
:param action: The resource to access (i.e. 'network/vlan')
:type action: ``str``
:param params: Parameters to give to the action
:type params: ``dict`` or ``None``
:param data: The data payload to be added to the request
:type data: ``str``
:param headers: Additional header to be added to the request
:type headers: ``str`` or ``dict`` or ``None``
:param method: HTTP Method for the request (i.e. 'GET', 'POST')
:type method: ``str``
:param page_size: The size of each page to be returned
Note: Max page size in MCP2.0 is currently 250
:type page_size: ``int``
"""
if params is None:
params = {}
params["pageSize"] = page_size
resp = self.request_with_orgId_api_2(
action, params, data, headers, method
).object
yield resp
if len(resp) <= 0:
return
pcount = resp.get("pageCount") # pylint: disable=no-member
psize = resp.get("pageSize") # pylint: disable=no-member
pnumber = resp.get("pageNumber") # pylint: disable=no-member
while int(pcount) >= int(psize):
params["pageNumber"] = int(pnumber) + 1
resp = self.request_with_orgId_api_2(
action, params, data, headers, method
).object
pcount = resp.get("pageCount") # pylint: disable=no-member
psize = resp.get("pageSize") # pylint: disable=no-member
pnumber = resp.get("pageNumber") # pylint: disable=no-member
yield resp
def get_resource_path_api_1(self):
"""
This method returns a resource path which is necessary for referencing
resources that require a full path instead of just an ID, such as
networks, and customer snapshots.
"""
return "%s/%s/%s" % (
self.api_path_version_1,
self.api_version_1,
self._get_orgId(),
)
def get_resource_path_api_2(self):
"""
This method returns a resource path which is necessary for referencing
resources that require a full path instead of just an ID, such as
networks, and customer snapshots.
"""
return "%s/%s/%s" % (
self.api_path_version_2,
self.active_api_version,
self._get_orgId(),
)
def wait_for_state(self, state, func, poll_interval=2, timeout=60, *args, **kwargs):
"""
Wait for the function which returns a instance with field status/state
to match.
Keep polling func until one of the desired states is matched
:param state: Either the desired state (`str`) or a `list` of states
:type state: ``str`` or ``list``
:param func: The function to call, e.g. ex_get_vlan. Note: This
function needs to return an object which has ``status``
attribute.
:type func: ``function``
:param poll_interval: The number of seconds to wait between checks
:type poll_interval: `int`
:param timeout: The total number of seconds to wait to reach a state
:type timeout: `int`
:param args: The arguments for func
:type args: Positional arguments
:param kwargs: The arguments for func
:type kwargs: Keyword arguments
:return: Result from the calling function.
"""
cnt = 0
result = None
object_state = None
while cnt < timeout / poll_interval:
result = func(*args, **kwargs)
if isinstance(result, Node):
object_state = result.state
else:
object_state = result.status
if object_state is state or str(object_state) in state:
return result
sleep(poll_interval)
cnt += 1
msg = "Status check for object %s timed out" % (result)
raise DimensionDataAPIException(code=object_state, msg=msg, driver=self.driver)
def _get_orgId(self):
"""
Send the /myaccount API request to DimensionData cloud and parse the
'orgId' from the XML response object. We need the orgId to use most
of the other API functions
"""
if self._orgId is None:
body = self.request_api_1("myaccount").object
self._orgId = findtext(body, "orgId", DIRECTORY_NS)
return self._orgId
def get_account_details(self):
"""
Get the details of this account
:rtype: :class:`DimensionDataAccountDetails`
"""
body = self.request_api_1("myaccount").object
return DimensionDataAccountDetails(
user_name=findtext(body, "userName", DIRECTORY_NS),
full_name=findtext(body, "fullName", DIRECTORY_NS),
first_name=findtext(body, "firstName", DIRECTORY_NS),
last_name=findtext(body, "lastName", DIRECTORY_NS),
email=findtext(body, "emailAddress", DIRECTORY_NS),
)
class DimensionDataAccountDetails(object):
"""
Dimension Data account class details
"""
def __init__(self, user_name, full_name, first_name, last_name, email):
self.user_name = user_name
self.full_name = full_name
self.first_name = first_name
self.last_name = last_name
self.email = email
class DimensionDataStatus(object):
"""
DimensionData API pending operation status class
action, request_time, user_name, number_of_steps, update_time,
step.name, step.number, step.percent_complete, failure_reason,
"""
def __init__(
self,
action=None,
request_time=None,
user_name=None,
number_of_steps=None,
update_time=None,
step_name=None,
step_number=None,
step_percent_complete=None,
failure_reason=None,
):
self.action = action
self.request_time = request_time
self.user_name = user_name
self.number_of_steps = number_of_steps
self.update_time = update_time
self.step_name = step_name
self.step_number = step_number
self.step_percent_complete = step_percent_complete
self.failure_reason = failure_reason
def __repr__(self):
return (
"<DimensionDataStatus: action=%s, request_time=%s, "
"user_name=%s, number_of_steps=%s, update_time=%s, "
"step_name=%s, step_number=%s, "
"step_percent_complete=%s, failure_reason=%s>"
) % (
self.action,
self.request_time,
self.user_name,
self.number_of_steps,
self.update_time,
self.step_name,
self.step_number,
self.step_percent_complete,
self.failure_reason,
)
class DimensionDataNetwork(object):
"""
DimensionData network with location.
"""
def __init__(self, id, name, description, location, private_net, multicast, status):
self.id = str(id)
self.name = name
self.description = description
self.location = location
self.private_net = private_net
self.multicast = multicast
self.status = status
def __repr__(self):
return (
"<DimensionDataNetwork: id=%s, name=%s, description=%s, "
"location=%s, private_net=%s, multicast=%s>"
) % (
self.id,
self.name,
self.description,
self.location,
self.private_net,
self.multicast,
)
class DimensionDataNetworkDomain(object):
"""
DimensionData network domain with location.
"""
def __init__(self, id, name, description, location, status, plan):
self.id = str(id)
self.name = name
self.description = description
self.location = location
self.status = status
self.plan = plan
def __repr__(self):
return (
"<DimensionDataNetworkDomain: id=%s, name=%s, "
"description=%s, location=%s, status=%s, plan=%s>"
) % (
self.id,
self.name,
self.description,
self.location,
self.status,
self.plan,
)
class DimensionDataPublicIpBlock(object):
"""
DimensionData Public IP Block with location.
"""
def __init__(self, id, base_ip, size, location, network_domain, status):
self.id = str(id)
self.base_ip = base_ip
self.size = size
self.location = location
self.network_domain = network_domain
self.status = status
def __repr__(self):
return (
"<DimensionDataNetworkDomain: id=%s, base_ip=%s, "
"size=%s, location=%s, status=%s>"
) % (self.id, self.base_ip, self.size, self.location, self.status)
class DimensionDataServerCpuSpecification(object):
"""
A class that represents the specification of the CPU(s) for a
node
"""
def __init__(self, cpu_count, cores_per_socket, performance):
"""
Instantiate a new :class:`DimensionDataServerCpuSpecification`
:param cpu_count: The number of CPUs
:type cpu_count: ``int``
:param cores_per_socket: The number of cores per socket, the
recommendation is 1
:type cores_per_socket: ``int``
:param performance: The performance type, e.g. HIGHPERFORMANCE
:type performance: ``str``
"""
self.cpu_count = cpu_count
self.cores_per_socket = cores_per_socket
self.performance = performance
def __repr__(self):
return (
"<DimensionDataServerCpuSpecification: "
"cpu_count=%s, cores_per_socket=%s, "
"performance=%s>"
) % (self.cpu_count, self.cores_per_socket, self.performance)
class DimensionDataServerDisk(object):
"""
A class that represents the disk on a server
"""
def __init__(self, id=None, scsi_id=None, size_gb=None, speed=None, state=None):
"""
Instantiate a new :class:`DimensionDataServerDisk`
:param id: The id of the disk
:type id: ``str``
:param scsi_id: Representation for scsi
:type scsi_id: ``int``
:param size_gb: Size of the disk
:type size_gb: ``int``
:param speed: Speed of the disk (i.e. STANDARD)
:type speed: ``str``
:param state: State of the disk (i.e. PENDING)
:type state: ``str``
"""
self.id = id
self.scsi_id = scsi_id
self.size_gb = size_gb
self.speed = speed
self.state = state
def __repr__(self):
return ("<DimensionDataServerDisk: " "id=%s, size_gb=%s") % (
self.id,
self.size_gb,
)
class DimensionDataServerVMWareTools(object):
"""
A class that represents the VMWareTools for a node
"""
def __init__(self, status, version_status, api_version):
"""
Instantiate a new :class:`DimensionDataServerVMWareTools` object
:param status: The status of VMWare Tools
:type status: ``str``
:param version_status: The status for the version of VMWare Tools
(i.e NEEDS_UPGRADE)
:type version_status: ``str``
:param api_version: The API version of VMWare Tools
:type api_version: ``str``
"""
self.status = status
self.version_status = version_status
self.api_version = api_version
def __repr__(self):
return (
"<DimensionDataServerVMWareTools "
"status=%s, version_status=%s, "
"api_version=%s>"
) % (self.status, self.version_status, self.api_version)
class DimensionDataFirewallRule(object):
"""
DimensionData Firewall Rule for a network domain
"""
def __init__(
self,
id,
name,
action,
location,
network_domain,
status,
ip_version,
protocol,
source,
destination,
enabled,
):
self.id = str(id)
self.name = name
self.action = action
self.location = location
self.network_domain = network_domain
self.status = status
self.ip_version = ip_version
self.protocol = protocol
self.source = source
self.destination = destination
self.enabled = enabled
def __repr__(self):
return (
"<DimensionDataFirewallRule: id=%s, name=%s, "
"action=%s, location=%s, network_domain=%s, "
"status=%s, ip_version=%s, protocol=%s, source=%s, "
"destination=%s, enabled=%s>"
) % (
self.id,
self.name,
self.action,
self.location,
self.network_domain,
self.status,
self.ip_version,
self.protocol,
self.source,
self.destination,
self.enabled,
)
class DimensionDataFirewallAddress(object):
"""
The source or destination model in a firewall rule
"""
def __init__(
self,
any_ip,
ip_address,
ip_prefix_size,
port_begin,
port_end,
address_list_id,
port_list_id,
):
self.any_ip = any_ip
self.ip_address = ip_address
self.ip_prefix_size = ip_prefix_size
self.port_list_id = port_list_id
self.port_begin = port_begin
self.port_end = port_end
self.address_list_id = address_list_id
self.port_list_id = port_list_id
def __repr__(self):
return (
"<DimensionDataFirewallAddress: any_ip=%s, ip_address=%s, "
"ip_prefix_size=%s, port_begin=%s, port_end=%s, "
"address_list_id=%s, port_list_id=%s>"
% (
self.any_ip,
self.ip_address,
self.ip_prefix_size,
self.port_begin,
self.port_end,
self.address_list_id,
self.port_list_id,
)
)
class DimensionDataNatRule(object):
"""
An IP NAT rule in a network domain
"""
def __init__(self, id, network_domain, internal_ip, external_ip, status):
self.id = id
self.network_domain = network_domain
self.internal_ip = internal_ip
self.external_ip = external_ip
self.status = status
def __repr__(self):
return ("<DimensionDataNatRule: id=%s, status=%s>") % (self.id, self.status)
class DimensionDataAntiAffinityRule(object):
"""
Anti-Affinity rule for DimensionData
An Anti-Affinity rule ensures that servers in the rule will
not reside on the same VMware ESX host.
"""
def __init__(self, id, node_list):
"""
Instantiate a new :class:`DimensionDataAntiAffinityRule`
:param id: The ID of the Anti-Affinity rule
:type id: ``str``
:param node_list: List of node ids that belong in this rule
:type node_list: ``list`` of ``str``
"""
self.id = id
self.node_list = node_list
def __repr__(self):
return ("<DimensionDataAntiAffinityRule: id=%s>") % (self.id)
class DimensionDataVlan(object):
"""
DimensionData VLAN.
"""
def __init__(
self,
id,
name,
description,
location,
network_domain,
status,
private_ipv4_range_address,
private_ipv4_range_size,
ipv6_range_address,
ipv6_range_size,
ipv4_gateway,
ipv6_gateway,
):
"""
Initialize an instance of ``DimensionDataVlan``
:param id: The ID of the VLAN
:type id: ``str``
:param name: The name of the VLAN
:type name: ``str``
:param description: Plan text description of the VLAN
:type description: ``str``
:param location: The location (data center) of the VLAN
:type location: ``NodeLocation``
:param network_domain: The Network Domain that owns this VLAN
:type network_domain: :class:`DimensionDataNetworkDomain`
:param status: The status of the VLAN
:type status: :class:`DimensionDataStatus`
:param private_ipv4_range_address: The host address of the VLAN
IP space
:type private_ipv4_range_address: ``str``
:param private_ipv4_range_size: The size (e.g. '24') of the VLAN
as a CIDR range size
:type private_ipv4_range_size: ``int``
:param ipv6_range_address: The host address of the VLAN
IP space
:type ipv6_range_address: ``str``
:param ipv6_range_size: The size (e.g. '32') of the VLAN
as a CIDR range size
:type ipv6_range_size: ``int``
:param ipv4_gateway: The IPv4 default gateway address
:type ipv4_gateway: ``str``
:param ipv6_gateway: The IPv6 default gateway address
:type ipv6_gateway: ``str``
"""
self.id = str(id)
self.name = name
self.location = location
self.description = description
self.network_domain = network_domain
self.status = status
self.private_ipv4_range_address = private_ipv4_range_address
self.private_ipv4_range_size = private_ipv4_range_size
self.ipv6_range_address = ipv6_range_address
self.ipv6_range_size = ipv6_range_size
self.ipv4_gateway = ipv4_gateway
self.ipv6_gateway = ipv6_gateway
def __repr__(self):
return (
"<DimensionDataVlan: id=%s, name=%s, "
"description=%s, location=%s, status=%s>"
) % (self.id, self.name, self.description, self.location, self.status)
class DimensionDataPool(object):
"""
DimensionData VIP Pool.
"""
def __init__(
self,
id,
name,
description,
status,
load_balance_method,
health_monitor_id,
service_down_action,
slow_ramp_time,
):
"""
Initialize an instance of ``DimensionDataPool``
:param id: The ID of the pool
:type id: ``str``
:param name: The name of the pool
:type name: ``str``
:param description: Plan text description of the pool
:type description: ``str``
:param status: The status of the pool
:type status: :class:`DimensionDataStatus`
:param load_balance_method: The load balancer method
:type load_balance_method: ``str``
:param health_monitor_id: The ID of the health monitor
:type health_monitor_id: ``str``
:param service_down_action: Action to take when pool is down
:type service_down_action: ``str``
:param slow_ramp_time: The ramp-up time for service recovery
:type slow_ramp_time: ``int``
"""
self.id = str(id)
self.name = name
self.description = description
self.status = status
self.load_balance_method = load_balance_method
self.health_monitor_id = health_monitor_id
self.service_down_action = service_down_action
self.slow_ramp_time = slow_ramp_time
def __repr__(self):
return ("<DimensionDataPool: id=%s, name=%s, " "description=%s, status=%s>") % (
self.id,
self.name,
self.description,
self.status,
)
class DimensionDataPoolMember(object):
"""
DimensionData VIP Pool Member.
"""
def __init__(self, id, name, status, ip, port, node_id):
"""
Initialize an instance of ``DimensionDataPoolMember``
:param id: The ID of the pool member
:type id: ``str``
:param name: The name of the pool member
:type name: ``str``
:param status: The status of the pool
:type status: :class:`DimensionDataStatus`
:param ip: The IP of the pool member
:type ip: ``str``
:param port: The port of the pool member
:type port: ``int``
:param node_id: The ID of the associated node
:type node_id: ``str``
"""
self.id = str(id)
self.name = name
self.status = status
self.ip = ip
self.port = port
self.node_id = node_id
def __repr__(self):
return (
"<DimensionDataPoolMember: id=%s, name=%s, "
"ip=%s, status=%s, port=%s, node_id=%s>"
) % (self.id, self.name, self.ip, self.status, self.port, self.node_id)
class DimensionDataVIPNode(object):
def __init__(
self,
id,
name,
status,
ip,
connection_limit="10000",
connection_rate_limit="10000",
):
"""
Initialize an instance of :class:`DimensionDataVIPNode`
:param id: The ID of the node
:type id: ``str``
:param name: The name of the node
:type name: ``str``
:param status: The status of the node
:type status: :class:`DimensionDataStatus`
:param ip: The IP of the node
:type ip: ``str``
:param connection_limit: The total connection limit for the node
:type connection_limit: ``int``
:param connection_rate_limit: The rate limit for the node
:type connection_rate_limit: ``int``
"""
self.id = str(id)
self.name = name
self.status = status
self.ip = ip
self.connection_limit = connection_limit
self.connection_rate_limit = connection_rate_limit
def __repr__(self):
return ("<DimensionDataVIPNode: id=%s, name=%s, " "status=%s, ip=%s>") % (
self.id,
self.name,
self.status,
self.ip,
)
class DimensionDataVirtualListener(object):
"""
DimensionData Virtual Listener.
"""
def __init__(self, id, name, status, ip):
"""
Initialize an instance of :class:`DimensionDataVirtualListener`
:param id: The ID of the listener
:type id: ``str``
:param name: The name of the listener
:type name: ``str``
:param status: The status of the listener
:type status: :class:`DimensionDataStatus`
:param ip: The IP of the listener
:type ip: ``str``
"""
self.id = str(id)
self.name = name
self.status = status
self.ip = ip
def __repr__(self):
return (
"<DimensionDataVirtualListener: id=%s, name=%s, " "status=%s, ip=%s>"
) % (self.id, self.name, self.status, self.ip)
class DimensionDataDefaultHealthMonitor(object):
"""
A default health monitor for a VIP (node, pool or listener)
"""
def __init__(self, id, name, node_compatible, pool_compatible):
"""
Initialize an instance of :class:`DimensionDataDefaultHealthMonitor`
:param id: The ID of the monitor
:type id: ``str``
:param name: The name of the monitor
:type name: ``str``
:param node_compatible: Is a monitor capable of monitoring nodes
:type node_compatible: ``bool``
:param pool_compatible: Is a monitor capable of monitoring pools
:type pool_compatible: ``bool``
"""
self.id = id
self.name = name
self.node_compatible = node_compatible
self.pool_compatible = pool_compatible
def __repr__(self):
return ("<DimensionDataDefaultHealthMonitor: id=%s, name=%s>") % (
self.id,
self.name,
)
class DimensionDataPersistenceProfile(object):
"""
Each Persistence Profile declares the combination of Virtual Listener
type and protocol with which it is
compatible and whether or not it is compatible as a
Fallback Persistence Profile.
"""
def __init__(self, id, name, compatible_listeners, fallback_compatible):
"""
Initialize an instance of :class:`DimensionDataPersistenceProfile`
:param id: The ID of the profile
:type id: ``str``
:param name: The name of the profile
:type name: ``str``
:param compatible_listeners: List of compatible Virtual Listener types
:type compatible_listeners: ``list`` of
:class:`DimensionDataVirtualListenerCompatibility`
:param fallback_compatible: Is capable as a fallback profile
:type fallback_compatible: ``bool``
"""
self.id = id
self.name = name
self.compatible_listeners = compatible_listeners
self.fallback_compatible = fallback_compatible
def __repr__(self):
return ("<DimensionDataPersistenceProfile: id=%s, name=%s>") % (
self.id,
self.name,
)
class DimensionDataDefaultiRule(object):
"""
A default iRule for a network domain, can be applied to a listener
"""
def __init__(self, id, name, compatible_listeners):
"""
Initialize an instance of :class:`DimensionDataDefaultiRule`
:param id: The ID of the iRule
:type id: ``str``
:param name: The name of the iRule
:type name: ``str``
:param compatible_listeners: List of compatible Virtual Listener types
:type compatible_listeners: ``list`` of
:class:`DimensionDataVirtualListenerCompatibility`
"""
self.id = id
self.name = name
self.compatible_listeners = compatible_listeners
def __repr__(self):
return ("<DimensionDataDefaultiRule: id=%s, name=%s>") % (self.id, self.name)
class DimensionDataVirtualListenerCompatibility(object):
"""
A compatibility preference for a persistence profile or iRule
specifies which virtual listener types this profile or iRule can be
applied to.
"""
def __init__(self, type, protocol):
self.type = type
self.protocol = protocol
def __repr__(self):
return (
"<DimensionDataVirtualListenerCompatibility: " "type=%s, protocol=%s>"
) % (self.type, self.protocol)
class DimensionDataBackupDetails(object):
"""
Dimension Data Backup Details represents information about
a targets backups configuration
"""
def __init__(self, asset_id, service_plan, status, clients=None):
"""
Initialize an instance of :class:`DimensionDataBackupDetails`
:param asset_id: Asset identification for backups
:type asset_id: ``str``
:param service_plan: The service plan for backups. i.e (Essentials)
:type service_plan: ``str``
:param status: The overall status this backup target.
i.e. (unregistered)
:type status: ``str``
:param clients: Backup clients attached to this target
:type clients: ``list`` of :class:`DimensionDataBackupClient`
"""
self.asset_id = asset_id
self.service_plan = service_plan
self.status = status
self.clients = clients
def __repr__(self):
return ("<DimensionDataBackupDetails: id=%s>") % (self.asset_id)
class DimensionDataBackupClient(object):
"""
An object that represents a backup client
"""
def __init__(
self,
id,
type,
status,
schedule_policy,
storage_policy,
download_url,
alert=None,
running_job=None,
):
"""
Initialize an instance of :class:`DimensionDataBackupClient`
:param id: Unique ID for the client
:type id: ``str``
:param type: The type of client that this client is
:type type: :class:`DimensionDataBackupClientType`
:param status: The states of this particular backup client.
i.e. (Unregistered)
:type status: ``str``
:param schedule_policy: The schedule policy for this client
NOTE: Dimension Data only sends back the name
of the schedule policy, no further details
:type schedule_policy: ``str``
:param storage_policy: The storage policy for this client
NOTE: Dimension Data only sends back the name
of the storage policy, no further details
:type storage_policy: ``str``
:param download_url: The download url for this client
:type download_url: ``str``
:param alert: The alert configured for this backup client (optional)
:type alert: :class:`DimensionDataBackupClientAlert`
:param alert: The running job for the client (optional)
:type alert: :class:`DimensionDataBackupClientRunningJob`
"""
self.id = id
self.type = type
self.status = status
self.schedule_policy = schedule_policy
self.storage_policy = storage_policy
self.download_url = download_url
self.alert = alert
self.running_job = running_job
def __repr__(self):
return ("<DimensionDataBackupClient: id=%s>") % (self.id)
class DimensionDataBackupClientAlert(object):
"""
An alert for a backup client
"""
def __init__(self, trigger, notify_list=[]):
"""
Initialize an instance of :class:`DimensionDataBackupClientAlert`
:param trigger: Trigger type for the client i.e. ON_FAILURE
:type trigger: ``str``
:param notify_list: List of email addresses that are notified
when the alert is fired
:type notify_list: ``list`` of ``str``
"""
self.trigger = trigger
self.notify_list = notify_list
def __repr__(self):
return ("<DimensionDataBackupClientAlert: trigger=%s>") % (self.trigger)
class DimensionDataBackupClientRunningJob(object):
"""
A running job for a given backup client
"""
def __init__(self, id, status, percentage=0):
"""
Initialize an instance of :class:`DimensionDataBackupClientRunningJob`
:param id: The unqiue ID of the job
:type id: ``str``
:param status: The status of the job i.e. Waiting
:type status: ``str``
:param percentage: The percentage completion of the job
:type percentage: ``int``
"""
self.id = id
self.percentage = percentage
self.status = status
def __repr__(self):
return ("<DimensionDataBackupClientRunningJob: id=%s>") % (self.id)
class DimensionDataBackupClientType(object):
"""
A client type object for backups
"""
def __init__(self, type, is_file_system, description):
"""
Initialize an instance of :class:`DimensionDataBackupClientType`
:param type: The type of client i.e. (FA.Linux, MySQL, ect.)
:type type: ``str``
:param is_file_system: The name of the iRule
:type is_file_system: ``bool``
:param description: Description of the client
:type description: ``str``
"""
self.type = type
self.is_file_system = is_file_system
self.description = description
def __repr__(self):
return ("<DimensionDataBackupClientType: type=%s>") % (self.type)
class DimensionDataBackupStoragePolicy(object):
"""
A representation of a storage policy
"""
def __init__(self, name, retention_period, secondary_location):
"""
Initialize an instance of :class:`DimensionDataBackupStoragePolicy`
:param name: The name of the storage policy i.e. 14 Day Storage Policy
:type name: ``str``
:param retention_period: How long to keep the backup in days
:type retention_period: ``int``
:param secondary_location: The secondary location i.e. Primary
:type secondary_location: ``str``
"""
self.name = name
self.retention_period = retention_period
self.secondary_location = secondary_location
def __repr__(self):
return ("<DimensionDataBackupStoragePolicy: name=%s>") % (self.name)
class DimensionDataBackupSchedulePolicy(object):
"""
A representation of a schedule policy
"""
def __init__(self, name, description):
"""
Initialize an instance of :class:`DimensionDataBackupSchedulePolicy`
:param name: The name of the policy i.e 12AM - 6AM
:type name: ``str``
:param description: Short summary of the details of the policy
:type description: ``str``
"""
self.name = name
self.description = description
def __repr__(self):
return ("<DimensionDataBackupSchedulePolicy: name=%s>") % (self.name)
class DimensionDataTag(object):
"""
A representation of a Tag in Dimension Data
A Tag first must have a Tag Key, then an asset is tag with
a key and an option value. Tags can be queried later to filter assets
and also show up on usage report if so desired.
"""
def __init__(self, asset_type, asset_id, asset_name, datacenter, key, value):
"""
Initialize an instance of :class:`DimensionDataTag`
:param asset_type: The type of asset. Current asset types:
SERVER, VLAN, NETWORK_DOMAIN, CUSTOMER_IMAGE,
PUBLIC_IP_BLOCK, ACCOUNT
:type asset_type: ``str``
:param asset_id: The GUID of the asset that is tagged
:type asset_id: ``str``
:param asset_name: The name of the asset that is tagged
:type asset_name: ``str``
:param datacenter: The short datacenter name of the tagged asset
:type datacenter: ``str``
:param key: The tagged key
:type key: :class:`DimensionDataTagKey`
:param value: The tagged value
:type value: ``None`` or ``str``
"""
self.asset_type = asset_type
self.asset_id = asset_id
self.asset_name = asset_name
self.datacenter = datacenter
self.key = key
self.value = value
def __repr__(self):
return ("<DimensionDataTag: asset_name=%s, tag_name=%s, value=%s>") % (
self.asset_name,
self.key.name,
self.value,
)
class DimensionDataTagKey(object):
"""
A representation of a Tag Key in Dimension Data
A tag key is required to tag an asset
"""
def __init__(self, id, name, description, value_required, display_on_report):
"""
Initialize an instance of :class:`DimensionDataTagKey`
:param id: GUID of the tag key
:type id: ``str``
:param name: Name of the tag key
:type name: ``str``
:param description: Description of the tag key
:type description: ``str``
:param value_required: If a value is required for this tag key
:type value_required: ``bool``
:param display_on_report: If this tag key should be displayed on
usage reports
:type display_on_report: ``bool``
"""
self.id = id
self.name = name
self.description = description
self.value_required = value_required
self.display_on_report = display_on_report
def __repr__(self):
return ("<DimensionDataTagKey: name=%s>") % (self.name)
class DimensionDataIpAddressList(object):
"""
DimensionData IP Address list
"""
def __init__(
self,
id,
name,
description,
ip_version,
ip_address_collection,
state,
create_time,
child_ip_address_lists=None,
):
""""
Initialize an instance of :class:`DimensionDataIpAddressList`
:param id: GUID of the IP Address List key
:type id: ``str``
:param name: Name of the IP Address List
:type name: ``str``
:param description: Description of the IP Address List
:type description: ``str``
:param ip_version: IP version. E.g. IPV4, IPV6
:type ip_version: ``str``
:param ip_address_collection: Collection of DimensionDataIpAddress
:type ip_address_collection: ``List``
:param state: IP Address list state
:type state: ``str``
:param create_time: IP Address List created time
:type create_time: ``date time``
:param child_ip_address_lists: List of IP address list to be included
:type child_ip_address_lists: List \
of :class:'DimensionDataIpAddressList'
"""
self.id = id
self.name = name
self.description = description
self.ip_version = ip_version
self.ip_address_collection = ip_address_collection
self.state = state
self.create_time = create_time
self.child_ip_address_lists = child_ip_address_lists
def __repr__(self):
return (
"<DimensionDataIpAddressList: id=%s, name=%s, description=%s, "
"ip_version=%s, ip_address_collection=%s, state=%s, "
"create_time=%s, child_ip_address_lists=%s>"
% (
self.id,
self.name,
self.description,
self.ip_version,
self.ip_address_collection,
self.state,
self.create_time,
self.child_ip_address_lists,
)
)
class DimensionDataChildIpAddressList(object):
"""
DimensionData Child IP Address list
"""
def __init__(self, id, name):
""" "
Initialize an instance of :class:`DimensionDataChildIpAddressList`
:param id: GUID of the IP Address List key
:type id: ``str``
:param name: Name of the IP Address List
:type name: ``str``
"""
self.id = id
self.name = name
def __repr__(self):
return "<DimensionDataChildIpAddressList: id=%s, name=%s>" % (
self.id,
self.name,
)
class DimensionDataIpAddress(object):
"""
A representation of IP Address in Dimension Data
"""
def __init__(self, begin, end=None, prefix_size=None):
"""
Initialize an instance of :class:`DimensionDataIpAddress`
:param begin: IP Address Begin
:type begin: ``str``
:param end: IP Address end
:type end: ``str``
:param prefixSize: IP Address prefix size
:type prefixSize: ``int``
"""
self.begin = begin
self.end = end
self.prefix_size = prefix_size
def __repr__(self):
return "<DimensionDataIpAddress: begin=%s, end=%s, prefix_size=%s>" % (
self.begin,
self.end,
self.prefix_size,
)
class DimensionDataPortList(object):
"""
DimensionData Port list
"""
def __init__(
self,
id,
name,
description,
port_collection,
child_portlist_list,
state,
create_time,
):
""" "
Initialize an instance of :class:`DimensionDataPortList`
:param id: GUID of the Port List key
:type id: ``str``
:param name: Name of the Port List
:type name: ``str``
:param description: Description of the Port List
:type description: ``str``
:param port_collection: Collection of DimensionDataPort
:type port_collection: ``List``
:param child_portlist_list: Collection of DimensionDataChildPort
:type child_portlist_list: ``List``
:param state: Port list state
:type state: ``str``
:param create_time: Port List created time
:type create_time: ``date time``
"""
self.id = id
self.name = name
self.description = description
self.port_collection = port_collection
self.child_portlist_list = child_portlist_list
self.state = state
self.create_time = create_time
def __repr__(self):
return (
"<DimensionDataPortList: id=%s, name=%s, description=%s, "
"port_collection=%s, child_portlist_list=%s, state=%s, "
"create_time=%s>"
% (
self.id,
self.name,
self.description,
self.port_collection,
self.child_portlist_list,
self.state,
self.create_time,
)
)
class DimensionDataChildPortList(object):
"""
DimensionData Child Port list
"""
def __init__(self, id, name):
""" "
Initialize an instance of :class:`DimensionDataChildIpAddressList`
:param id: GUID of the child port list key
:type id: ``str``
:param name: Name of the child port List
:type name: ``str``
"""
self.id = id
self.name = name
def __repr__(self):
return "<DimensionDataChildPortList: id=%s, name=%s>" % (self.id, self.name)
class DimensionDataPort(object):
"""
A representation of Port in Dimension Data
"""
def __init__(self, begin, end=None):
"""
Initialize an instance of :class:`DimensionDataPort`
:param begin: Port Number Begin
:type begin: ``str``
:param end: Port Number end
:type end: ``str``
"""
self.begin = begin
self.end = end
def __repr__(self):
return "<DimensionDataPort: begin=%s, end=%s>" % (self.begin, self.end)
class DimensionDataNic(object):
"""
A representation of Network Adapter in Dimension Data
"""
def __init__(self, private_ip_v4=None, vlan=None, network_adapter_name=None):
"""
Initialize an instance of :class:`DimensionDataNic`
:param private_ip_v4: IPv4
:type private_ip_v4: ``str``
:param vlan: Network VLAN
:type vlan: class: DimensionDataVlan or ``str``
:param network_adapter_name: Network Adapter Name
:type network_adapter_name: ``str``
"""
self.private_ip_v4 = private_ip_v4
self.vlan = vlan
self.network_adapter_name = network_adapter_name
def __repr__(self):
return (
"<DimensionDataNic: private_ip_v4=%s, vlan=%s,"
"network_adapter_name=%s>"
% (self.private_ip_v4, self.vlan, self.network_adapter_name)
)