blob: ee76daf400fb1440fcfb01b7de7e4c096bddf33e [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 Driver
"""
try:
from lxml import etree as ET
except ImportError:
from xml.etree import ElementTree as ET
from libcloud.compute.base import NodeDriver, Node
from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
from libcloud.common.dimensiondata import (DimensionDataConnection,
DimensionDataStatus)
from libcloud.common.dimensiondata import DimensionDataNetwork
from libcloud.common.dimensiondata import DimensionDataNetworkDomain
from libcloud.common.dimensiondata import DimensionDataVlan
from libcloud.common.dimensiondata import DimensionDataPublicIpBlock
from libcloud.common.dimensiondata import DimensionDataFirewallRule
from libcloud.common.dimensiondata import DimensionDataFirewallAddress
from libcloud.common.dimensiondata import DimensionDataNatRule
from libcloud.common.dimensiondata import NetworkDomainServicePlan
from libcloud.common.dimensiondata import API_ENDPOINTS
from libcloud.common.dimensiondata import DEFAULT_REGION
from libcloud.common.dimensiondata import TYPES_URN
from libcloud.common.dimensiondata import SERVER_NS
from libcloud.common.dimensiondata import NETWORK_NS
from libcloud.utils.xml import fixxpath, findtext, findall
from libcloud.compute.types import NodeState, Provider
class DimensionDataNodeDriver(NodeDriver):
"""
DimensionData node driver.
"""
selected_region = None
connectionCls = DimensionDataConnection
name = 'DimensionData'
website = 'http://www.dimensiondata.com/'
type = Provider.DIMENSIONDATA
features = {'create_node': ['password']}
api_version = 1.0
def __init__(self, key, secret=None, secure=True, host=None, port=None,
api_version=None, region=DEFAULT_REGION, **kwargs):
if region not in API_ENDPOINTS:
raise ValueError('Invalid region: %s' % (region))
self.selected_region = API_ENDPOINTS[region]
super(DimensionDataNodeDriver, self).__init__(key=key, secret=secret,
secure=secure, host=host,
port=port,
api_version=api_version,
region=region,
**kwargs)
def _ex_connection_class_kwargs(self):
"""
Add the region to the kwargs before the connection is instantiated
"""
kwargs = super(DimensionDataNodeDriver,
self)._ex_connection_class_kwargs()
kwargs['region'] = self.selected_region
return kwargs
def create_node(self, name, image, auth, ex_description,
ex_network=None, ex_network_domain=None,
ex_vlan=None,
ex_is_started=True, **kwargs):
"""
Create a new DimensionData node
:keyword name: String with a name for this new node (required)
:type name: ``str``
:keyword image: OS Image to boot on node. (required)
:type image: :class:`NodeImage`
:keyword auth: Initial authentication information for the
node (required)
:type auth: :class:`NodeAuthPassword`
:keyword ex_description: description for this node (required)
:type ex_description: ``str``
:keyword ex_network: Network to create the node within (required,
unless using Network Domain)
:type ex_network: :class:`DimensionDataNetwork`
:keyword ex_network_domain: Network Domain to create the node
(required unless using network)
:type ex_network_domain: :class:`DimensionDataNetworkDomain`
:keyword ex_vlan: VLAN to create the node within
(required unless using network)
:type ex_vlan: :class:`DimensionDataVlan`
:keyword ex_is_started: Start server after creation? default
true (required)
:type ex_is_started: ``bool``
:return: The newly created :class:`Node`.
:rtype: :class:`Node`
"""
password = None
auth_obj = self._get_and_check_auth(auth)
password = auth_obj.password
if not isinstance(ex_network, DimensionDataNetwork):
if not isinstance(ex_network_domain, DimensionDataNetworkDomain):
raise ValueError('ex_network must be of DimensionDataNetwork '
'type or ex_network_domain must be of '
'DimensionDataNetworkDomain type')
server_elm = ET.Element('deployServer', {'xmlns': TYPES_URN})
ET.SubElement(server_elm, "name").text = name
ET.SubElement(server_elm, "description").text = ex_description
ET.SubElement(server_elm, "imageId").text = image.id
ET.SubElement(server_elm, "start").text = str(ex_is_started).lower()
ET.SubElement(server_elm, "administratorPassword").text = password
if ex_network is not None:
network_elm = ET.SubElement(server_elm, "network")
ET.SubElement(network_elm, "networkId").text = ex_network.id
if ex_network_domain is not None:
network_inf_elm = ET.SubElement(server_elm, "networkInfo",
{'networkDomainId':
ex_network_domain.id})
pri_nic = ET.SubElement(network_inf_elm, "primaryNic")
ET.SubElement(pri_nic, "vlanId").text = ex_vlan.id
response = self.connection.request_with_orgId_api_2(
'server/deployServer',
method='POST',
data=ET.tostring(server_elm)).object
node_id = None
for info in findall(response, 'info', TYPES_URN):
if info.get('name') == 'serverId':
node_id = info.get('value')
node = self.ex_get_node_by_id(node_id)
if getattr(auth_obj, "generated", False):
node.extra['password'] = auth_obj.password
return node
def destroy_node(self, node):
request_elm = ET.Element('deleteServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/deleteServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def reboot_node(self, node):
request_elm = ET.Element('rebootServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/rebootServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def list_nodes(self):
nodes = self._to_nodes(
self.connection.request_with_orgId_api_2('server/server').object)
return nodes
def list_images(self, location=None):
"""
return a list of available images
Currently only returns the default 'base OS images' provided by
DimensionData. Customer images (snapshots) are not yet supported.
@inherits: :class:`NodeDriver.list_images`
"""
params = {}
if location is not None:
params['location'] = location.id
return self._to_base_images(
self.connection.request_api_1('base/imageWithDiskSpeed',
params=params)
.object)
def list_sizes(self, location=None):
"""
return a list of available sizes
Currently, the size of the node is dictated by the chosen OS base
image, they cannot be set explicitly.
@inherits: :class:`NodeDriver.list_sizes`
"""
return [
NodeSize(id=1,
name="default",
ram=0,
disk=0,
bandwidth=0,
price=0,
driver=self.connection.driver),
]
def list_locations(self):
"""
list locations (datacenters) available for instantiating servers and
networks.
@inherits: :class:`NodeDriver.list_locations`
"""
return self._to_locations(
self.connection
.request_with_orgId_api_2('infrastructure/datacenter').object)
def list_networks(self, location=None):
"""
List networks deployed across all data center locations for your
organization. The response includes the location of each network.
:keyword location: The location
:type location: :class:`NodeLocation`
:return: a list of DimensionDataNetwork objects
:rtype: ``list`` of :class:`DimensionDataNetwork`
"""
url_ext = ''
if location is not None:
url_ext = '/' + location.id
return self._to_networks(
self.connection
.request_with_orgId_api_1('networkWithLocation%s' % url_ext)
.object)
def _to_base_images(self, object):
images = []
locations = self.list_locations()
for element in object.findall(fixxpath("image", SERVER_NS)):
images.append(self._to_base_image(element, locations))
return images
def _to_base_image(self, element, locations):
# Eventually we will probably need multiple _to_image() functions
# that parse <ServerImage> differently than <DeployedImage>.
# DeployedImages are customer snapshot images, and ServerImages are
# 'base' images provided by DimensionData
location_id = element.get('location')
location = list(filter(lambda x: x.id == location_id,
locations))[0]
extra = {
'description': findtext(element, 'description', SERVER_NS),
'OS_type': findtext(element, 'operatingSystem/type', SERVER_NS),
'OS_displayName': findtext(element, 'operatingSystem/displayName',
SERVER_NS),
'cpuCount': findtext(element, 'cpuCount', SERVER_NS),
'resourcePath': findtext(element, 'resourcePath', SERVER_NS),
'memory': findtext(element, 'memory', SERVER_NS),
'osStorage': findtext(element, 'osStorage', SERVER_NS),
'additionalStorage': findtext(element, 'additionalStorage',
SERVER_NS),
'created': findtext(element, 'created', SERVER_NS),
'location': location,
}
return NodeImage(id=element.get('id'),
name=str(findtext(element, 'name', SERVER_NS)),
extra=extra,
driver=self.connection.driver)
def ex_start_node(self, node):
"""
Powers on an existing deployed server
:param node: Node which should be used
:type node: :class:`Node`
:rtype: ``bool``
"""
request_elm = ET.Element('startServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/startServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_shutdown_graceful(self, node):
"""
This function will attempt to "gracefully" stop a server by
initiating a shutdown sequence within the guest operating system.
A successful response on this function means the system has
successfully passed the request into the operating system.
:param node: Node which should be used
:type node: :class:`Node`
:rtype: ``bool``
"""
request_elm = ET.Element('shutdownServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/shutdownServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_power_off(self, node):
"""
This function will abruptly power-off a server. Unlike
ex_shutdown_graceful, success ensures the node will stop but some OS
and application configurations may be adversely affected by the
equivalent of pulling the power plug out of the machine.
:param node: Node which should be used
:type node: :class:`Node`
:rtype: ``bool``
"""
request_elm = ET.Element('powerOffServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/powerOffServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_reset(self, node):
"""
This function will abruptly reset a server. Unlike
reboot_node, success ensures the node will restart but some OS
and application configurations may be adversely affected by the
equivalent of pulling the power plug out of the machine.
:param node: Node which should be used
:type node: :class:`Node`
:rtype: ``bool``
"""
request_elm = ET.Element('resetServer',
{'xmlns': TYPES_URN, 'id': node.id})
body = self.connection.request_with_orgId_api_2(
'server/resetServer',
method='POST',
data=ET.tostring(request_elm)).object
response_code = findtext(body, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_attach_node_to_vlan(self, node, vlan):
request = ET.Element('addNic',
{'xmlns': TYPES_URN})
ET.SubElement(request, 'serverId').text = node.id
nic = ET.SubElement(request, 'nic')
ET.SubElement(nic, 'vlanId').text = vlan.id
response = self.connection.request_with_orgId_api_2(
'server/addNic',
method='POST',
data=ET.tostring(request)).object
response_code = findtext(response, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_destroy_nic(self, nic_id):
request = ET.Element('removeNic',
{'xmlns': TYPES_URN,
'id': nic_id})
response = self.connection.request_with_orgId_api_2(
'server/removeNic',
method='POST',
data=ET.tostring(request)).object
response_code = findtext(response, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_list_networks(self, location=None):
"""
List networks deployed across all data center locations for your
organization. The response includes the location of each network.
:return: a list of DimensionDataNetwork objects
:rtype: ``list`` of :class:`DimensionDataNetwork`
"""
params = {}
if location is not None:
params['location'] = location.id
response = self.connection \
.request_with_orgId_api_1('networkWithLocation',
params=params).object
return self._to_networks(response)
def ex_get_network_domain(self, network_domain_id):
locations = self.list_locations()
net = self.connection.request_with_orgId_api_2(
'network/networkDomain/%s' % network_domain_id).object
return self._to_network_domain(net, locations)
def ex_list_network_domains(self, location=None):
"""
List networks deployed across all data center locations for your
organization. The response includes the location of each network.
:return: a list of DimensionDataNetwork objects
:rtype: ``list`` of :class:`DimensionDataNetwork`
"""
params = {}
if location is not None:
params['datacenterId'] = location.id
response = self.connection \
.request_with_orgId_api_2('network/networkDomain',
params=params).object
return self._to_network_domains(response)
def ex_create_network_domain(self, location, name, service_plan,
description=None):
"""
Deploy a new network domain to a data center
"""
create_node = ET.Element('deployNetworkDomain', {'xmlns': TYPES_URN})
ET.SubElement(create_node, "datacenterId").text = location.id
ET.SubElement(create_node, "name").text = name
if description is not None:
ET.SubElement(create_node, "description").text = description
ET.SubElement(create_node, "type").text = service_plan
response = self.connection.request_with_orgId_api_2(
'network/deployNetworkDomain',
method='POST',
data=ET.tostring(create_node)).object
network_domain_id = None
for info in findall(response, 'info', TYPES_URN):
if info.get('name') == 'networkDomainId':
network_domain_id = info.get('value')
return DimensionDataNetworkDomain(
id=network_domain_id,
name=name,
description=description,
location=location,
status=NodeState.RUNNING,
plan=service_plan
)
def ex_update_network_domain(self, network_domain):
"""
Update the properties of a network domain
"""
edit_node = ET.Element('editNetworkDomain', {'xmlns': TYPES_URN})
edit_node.set('id', network_domain.id)
ET.SubElement(edit_node, "name").text = network_domain.name
if network_domain.description is not None:
ET.SubElement(edit_node, "description").text \
= network_domain.description
ET.SubElement(edit_node, "type").text = network_domain.plan
self.connection.request_with_orgId_api_2(
'network/editNetworkDomain',
method='POST',
data=ET.tostring(edit_node)).object
return network_domain
def ex_delete_network_domain(self, network_domain):
delete_node = ET.Element('deleteNetworkDomain', {'xmlns': TYPES_URN})
delete_node.set('id', network_domain.id)
result = self.connection.request_with_orgId_api_2(
'network/deleteNetworkDomain',
method='POST',
data=ET.tostring(delete_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_create_vlan(self,
network_domain,
name,
private_ipv4_base_address,
description=None,
private_ipv4_prefix_size='24'):
"""
Deploy a new VLAN to a network domain
"""
create_node = ET.Element('deployVlan', {'xmlns': TYPES_URN})
ET.SubElement(create_node, "networkDomainId").text = network_domain.id
ET.SubElement(create_node, "name").text = name
if description is not None:
ET.SubElement(create_node, "description").text = description
ET.SubElement(create_node, "privateIpv4BaseAddress").text = \
private_ipv4_base_address
ET.SubElement(create_node, "privateIpv4PrefixSize").text = \
private_ipv4_prefix_size
response = self.connection.request_with_orgId_api_2(
'network/deployVlan',
method='POST',
data=ET.tostring(create_node)).object
vlan_id = None
for info in findall(response, 'info', TYPES_URN):
if info.get('name') == 'vlanId':
vlan_id = info.get('value')
return DimensionDataVlan(
id=vlan_id,
name=name,
description=description,
location=network_domain.location,
status=NodeState.RUNNING,
private_ipv4_range_address=private_ipv4_base_address,
private_ipv4_range_size=private_ipv4_prefix_size
)
def ex_get_vlan(self, vlan_id):
locations = self.list_locations()
vlan = self.connection.request_with_orgId_api_2(
'network/vlan/%s' % vlan_id).object
return self._to_vlan(vlan, locations)
def ex_update_vlan(self, vlan):
"""
Updates the properties of the given VLAN
Only name and description are updated
"""
edit_node = ET.Element('editVlan', {'xmlns': TYPES_URN})
edit_node.set('id', vlan.id)
ET.SubElement(edit_node, "name").text = vlan.name
if vlan.description is not None:
ET.SubElement(edit_node, "description").text \
= vlan.description
self.connection.request_with_orgId_api_2(
'network/editVlan',
method='POST',
data=ET.tostring(edit_node)).object
return vlan
def ex_expand_vlan(self, vlan):
"""
Expands the VLAN to the prefix size in private_ipv4_range_size
The expansion will
not be permitted if the proposed IP space overlaps with an
already deployed VLANs IP space.
"""
edit_node = ET.Element('expandVlan', {'xmlns': TYPES_URN})
edit_node.set('id', vlan.id)
ET.SubElement(edit_node, "privateIpv4PrefixSize").text =\
vlan.private_ipv4_range_size
self.connection.request_with_orgId_api_2(
'network/expandVlan',
method='POST',
data=ET.tostring(edit_node)).object
return vlan
def ex_delete_vlan(self, vlan):
delete_node = ET.Element('deleteVlan', {'xmlns': TYPES_URN})
delete_node.set('id', vlan.id)
result = self.connection.request_with_orgId_api_2(
'network/deleteVlan',
method='POST',
data=ET.tostring(delete_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_list_vlans(self, location=None, network_domain=None):
"""
List VLANs available in a given networkDomain
:return: a list of DimensionDataVlan objects
:rtype: ``list`` of :class:`DimensionDataVlan`
"""
params = {}
if location is not None:
params['datacenterId'] = location.id
if network_domain is not None:
params['networkDomainId'] = network_domain.id
response = self.connection.request_with_orgId_api_2('network/vlan',
params=params) \
.object
return self._to_vlans(response)
def ex_add_public_ip_block_to_network_domain(self, network_domain):
add_node = ET.Element('addPublicIpBlock', {'xmlns': TYPES_URN})
ET.SubElement(add_node, "networkDomainId").text =\
network_domain.id
response = self.connection.request_with_orgId_api_2(
'network/addPublicIpBlock',
method='POST',
data=ET.tostring(add_node)).object
block_id = None
for info in findall(response, 'info', TYPES_URN):
if info.get('name') == 'publicIpBlockId':
block_id = info.get('value')
return self.ex_get_public_ip_block(block_id)
def ex_list_public_ip_blocks(self, network_domain):
params = {}
params['networkDomainId'] = network_domain.id
response = self.connection \
.request_with_orgId_api_2('network/publicIpBlock',
params=params).object
return self._to_ip_blocks(response)
def ex_get_public_ip_block(self, block_id):
locations = self.list_locations()
block = self.connection.request_with_orgId_api_2(
'network/publicIpBlock/%s' % block_id).object
return self._to_ip_block(block, locations)
def ex_delete_public_ip_block(self, block):
delete_node = ET.Element('removePublicIpBlock', {'xmlns': TYPES_URN})
delete_node.set('id', block.id)
result = self.connection.request_with_orgId_api_2(
'network/removePublicIpBlock',
method='POST',
data=ET.tostring(delete_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_get_node_by_id(self, id):
node = self.connection.request_with_orgId_api_2(
'server/server/%s' % id).object
return self._to_node(node)
def ex_list_firewall_rules(self, network_domain):
params = {}
params['networkDomainId'] = network_domain.id
response = self.connection \
.request_with_orgId_api_2('network/firewallRule',
params=params).object
return self._to_firewall_rules(response, network_domain)
def ex_create_firewall_rule(self, network_domain, rule, position):
create_node = ET.Element('createFirewallRule', {'xmlns': TYPES_URN})
ET.SubElement(create_node, "networkDomainId").text = network_domain.id
ET.SubElement(create_node, "name").text = rule.name
ET.SubElement(create_node, "action").text = rule.action
ET.SubElement(create_node, "ipVersion").text = rule.ip_version
ET.SubElement(create_node, "protocol").text = rule.protocol
# Setup source port rule
source = ET.SubElement(create_node, "source")
source_ip = ET.SubElement(source, 'ip')
if rule.source.any_ip:
source_ip.set('address', 'ANY')
else:
source_ip.set('address', rule.source.ip_address)
source_ip.set('prefixSize', rule.source.ip_prefix_size)
source_port = ET.SubElement(source, 'port')
source_port.set('begin', rule.source.port_begin)
if rule.source.port_end is not None:
source_port.set('end', rule.source.port_end)
# Setup destination port rule
dest = ET.SubElement(create_node, "destination")
dest_ip = ET.SubElement(dest, 'ip')
if rule.destination.any_ip:
dest_ip.set('address', 'ANY')
else:
dest_ip.set('address', rule.destination.ip_address)
dest_ip.set('prefixSize', rule.destination.ip_prefix_size)
dest_port = ET.SubElement(dest, 'port')
dest_port.set('begin', rule.destination.port_begin)
if rule.destination.port_end is not None:
dest_port.set('end', rule.destination.port_end)
ET.SubElement(create_node, "enabled").text = 'true'
placement = ET.SubElement(create_node, "placement")
placement.set('position', position)
response = self.connection.request_with_orgId_api_2(
'network/createFirewallRule',
method='POST',
data=ET.tostring(create_node)).object
rule_id = None
for info in findall(response, 'info', TYPES_URN):
if info.get('name') == 'firewallRuleId':
rule_id = info.get('value')
rule.id = rule_id
return rule
def ex_get_firewall_rule(self, network_domain, rule_id):
locations = self.list_locations()
rule = self.connection.request_with_orgId_api_2(
'network/firewallRule/%s' % rule_id).object
return self._to_firewall_rule(rule, locations, network_domain)
def ex_set_firewall_rule_state(self, rule, state):
"""
Change the state (enabled or disabled) of a rule
"""
update_node = ET.Element('editFirewallRule', {'xmlns': TYPES_URN})
update_node.set('id', rule.id)
ET.SubElement(update_node, 'enabled').text = str(state).lower()
result = self.connection.request_with_orgId_api_2(
'network/editFirewallRule',
method='POST',
data=ET.tostring(update_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_delete_firewall_rule(self, rule):
update_node = ET.Element('deleteFirewallRule', {'xmlns': TYPES_URN})
update_node.set('id', rule.id)
result = self.connection.request_with_orgId_api_2(
'network/deleteFirewallRule',
method='POST',
data=ET.tostring(update_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_create_nat_rule(self, network_domain, internal_ip, external_ip):
create_node = ET.Element('createNatRule', {'xmlns': TYPES_URN})
ET.SubElement(create_node, 'networkDomainId').text = network_domain.id
ET.SubElement(create_node, 'internalIp').text = internal_ip
ET.SubElement(create_node, 'externalIp').text = external_ip
result = self.connection.request_with_orgId_api_2(
'network/createNatRule',
method='POST',
data=ET.tostring(create_node)).object
rule_id = None
for info in findall(result, 'info', TYPES_URN):
if info.get('name') == 'natRuleId':
rule_id = info.get('value')
return DimensionDataNatRule(
id=rule_id,
network_domain=network_domain,
internal_ip=internal_ip,
external_ip=external_ip,
status=NodeState.RUNNING
)
def ex_list_nat_rules(self, network_domain):
params = {}
params['networkDomainId'] = network_domain.id
response = self.connection \
.request_with_orgId_api_2('network/natRule',
params=params).object
return self._to_nat_rules(response, network_domain)
def ex_get_nat_rule(self, network_domain, rule_id):
rule = self.connection.request_with_orgId_api_2(
'network/natRule/%s' % rule_id).object
return self._to_nat_rule(rule, network_domain)
def ex_delete_nat_rule(self, rule):
update_node = ET.Element('deleteNatRule', {'xmlns': TYPES_URN})
update_node.set('id', rule.id)
result = self.connection.request_with_orgId_api_2(
'network/deleteNatRule',
method='POST',
data=ET.tostring(update_node)).object
response_code = findtext(result, 'responseCode', TYPES_URN)
return response_code in ['IN_PROGRESS', 'OK']
def ex_get_location_by_id(self, id):
"""
Get location by ID.
:param id: ID of the node location which should be used
:type id: ``str``
:rtype: :class:`NodeLocation`
"""
location = None
if id is not None:
location = list(
filter(lambda x: x.id == id, self.list_locations()))[0]
return location
def _to_nat_rules(self, object, network_domain):
rules = []
for element in findall(object, 'natRule', TYPES_URN):
rules.append(
self._to_nat_rule(element, network_domain))
return rules
def _to_nat_rule(self, element, network_domain):
status = self._to_status(element.find(fixxpath('state', TYPES_URN)))
return DimensionDataNatRule(
id=element.get('id'),
network_domain=network_domain,
internal_ip=findtext(element, 'internalIp', TYPES_URN),
external_ip=findtext(element, 'externalIp', TYPES_URN),
status=status)
def _to_firewall_rules(self, object, network_domain):
rules = []
locations = self.list_locations()
for element in findall(object, 'firewallRule', TYPES_URN):
rules.append(
self._to_firewall_rule(element, locations, network_domain))
return rules
def _to_firewall_rule(self, element, locations, network_domain):
status = self._to_status(element.find(fixxpath('state', TYPES_URN)))
location_id = element.get('datacenterId')
location = list(filter(lambda x: x.id == location_id,
locations))[0]
return DimensionDataFirewallRule(
id=element.get('id'),
network_domain=network_domain,
name=findtext(element, 'name', TYPES_URN),
action=findtext(element, 'action', TYPES_URN),
ip_version=findtext(element, 'ipVersion', TYPES_URN),
protocol=findtext(element, 'protocol', TYPES_URN),
enabled=findtext(element, 'enabled', TYPES_URN),
source=self._to_firewall_address(
element.find(fixxpath('source', TYPES_URN))),
destination=self._to_firewall_address(
element.find(fixxpath('destination', TYPES_URN))),
location=location,
status=status)
def _to_firewall_address(self, element):
ip = element.find(fixxpath('ip', TYPES_URN))
port = element.find(fixxpath('port', TYPES_URN))
return DimensionDataFirewallAddress(
any_ip=ip.get('address') == 'ANY',
ip_address=ip.get('address'),
ip_prefix_size=ip.get('prefixSize'),
port_begin=port.get('begin') if port is not None else None,
port_end=port.get('end') if port is not None else None
)
def _to_ip_blocks(self, object):
blocks = []
locations = self.list_locations()
for element in findall(object, 'publicIpBlock', TYPES_URN):
blocks.append(self._to_ip_block(element, locations))
return blocks
def _to_ip_block(self, element, locations):
status = self._to_status(element.find(fixxpath('state', TYPES_URN)))
location_id = element.get('datacenterId')
location = list(filter(lambda x: x.id == location_id,
locations))[0]
return DimensionDataPublicIpBlock(
id=element.get('id'),
network_domain=self.ex_get_network_domain(
findtext(element, 'networkDomainId', TYPES_URN)
),
base_ip=findtext(element, 'baseIp', TYPES_URN),
size=findtext(element, 'size', TYPES_URN),
location=location,
status=status)
def _to_networks(self, object):
networks = []
locations = self.list_locations()
for element in findall(object, 'network', NETWORK_NS):
networks.append(self._to_network(element, locations))
return networks
def _to_network(self, element, locations):
multicast = False
if findtext(element, 'multicast', NETWORK_NS) == 'true':
multicast = True
status = self._to_status(element.find(fixxpath('status', NETWORK_NS)))
location_id = findtext(element, 'location', NETWORK_NS)
location = list(filter(lambda x: x.id == location_id,
locations))[0]
return DimensionDataNetwork(
id=findtext(element, 'id', NETWORK_NS),
name=findtext(element, 'name', NETWORK_NS),
description=findtext(element, 'description',
NETWORK_NS),
location=location,
private_net=findtext(element, 'privateNet',
NETWORK_NS),
multicast=multicast,
status=status)
def _to_network_domains(self, object):
network_domains = []
locations = self.list_locations()
for element in findall(object, 'networkDomain', TYPES_URN):
network_domains.append(self._to_network_domain(element, locations))
return network_domains
def _to_network_domain(self, element, locations):
status = self._to_status(element.find(fixxpath('state', TYPES_URN)))
location_id = element.get('datacenterId')
location = list(filter(lambda x: x.id == location_id,
locations))[0]
plan = findtext(element, 'type', TYPES_URN)
if plan is 'ESSENTIALS':
plan_type = NetworkDomainServicePlan.ESSENTIALS
else:
plan_type = NetworkDomainServicePlan.ADVANCED
return DimensionDataNetworkDomain(
id=element.get('id'),
name=findtext(element, 'name', TYPES_URN),
description=findtext(element, 'description', TYPES_URN),
plan=plan_type,
location=location,
status=status)
def _to_vlans(self, object):
vlans = []
locations = self.list_locations()
for element in findall(object, 'vlan', TYPES_URN):
vlans.append(self._to_vlan(element, locations=locations))
return vlans
def _to_vlan(self, element, locations):
status = self._to_status(element.find(fixxpath('state', TYPES_URN)))
location_id = element.get('datacenterId')
location = list(filter(lambda x: x.id == location_id,
locations))[0]
ip_range = element.find(fixxpath('privateIpv4Range', TYPES_URN))
return DimensionDataVlan(
id=element.get('id'),
name=findtext(element, 'name', TYPES_URN),
description=findtext(element, 'description',
TYPES_URN),
private_ipv4_range_address=ip_range.get('address'),
private_ipv4_range_size=ip_range.get('prefixSize'),
location=location,
status=status)
def _to_locations(self, object):
locations = []
for element in object.findall(fixxpath('datacenter', TYPES_URN)):
locations.append(self._to_location(element))
return locations
def _to_location(self, element):
l = NodeLocation(id=element.get('id'),
name=findtext(element, 'displayName', TYPES_URN),
country=findtext(element, 'country', TYPES_URN),
driver=self)
return l
def _to_nodes(self, object):
node_elements = object.findall(fixxpath('Server', TYPES_URN))
return [self._to_node(el) for el in node_elements]
def _to_node(self, element):
if findtext(element, 'started', TYPES_URN) == 'true':
state = NodeState.RUNNING
else:
state = NodeState.TERMINATED
status = self._to_status(element.find(fixxpath('progress', TYPES_URN)))
has_network_info \
= element.find(fixxpath('networkInfo', TYPES_URN)) is not None
extra = {
'description': findtext(element, 'description', TYPES_URN),
'sourceImageId': findtext(element, 'sourceImageId', TYPES_URN),
'networkId': findtext(element, 'networkId', TYPES_URN),
'networkDomainId':
element.find(fixxpath('networkInfo', TYPES_URN))
.get('networkDomainId')
if has_network_info else None,
'datacenterId': element.get('datacenterId'),
'deployedTime': findtext(element, 'createTime', TYPES_URN),
'cpuCount': int(findtext(
element,
'cpuCount',
TYPES_URN)),
'memoryMb': int(findtext(
element,
'memoryGb',
TYPES_URN)) * 1024,
'OS_id': element.find(fixxpath(
'operatingSystem',
TYPES_URN)).get('id'),
'OS_type': element.find(fixxpath(
'operatingSystem',
TYPES_URN)).get('family'),
'OS_displayName': element.find(fixxpath(
'operatingSystem',
TYPES_URN)).get('displayName'),
'status': status
}
public_ip = findtext(element, 'publicIpAddress', TYPES_URN)
private_ip = element.find(
fixxpath('networkInfo/primaryNic', TYPES_URN)) \
.get('privateIpv4') \
if has_network_info else \
element.find(fixxpath('nic', TYPES_URN)).get('privateIpv4')
n = Node(id=element.get('id'),
name=findtext(element, 'name', TYPES_URN),
state=state,
public_ips=[public_ip] if public_ip is not None else [],
private_ips=[private_ip] if private_ip is not None else [],
driver=self.connection.driver,
extra=extra)
return n
def _to_status(self, element):
if element is None:
return DimensionDataStatus()
s = DimensionDataStatus(action=findtext(element, 'action', TYPES_URN),
request_time=findtext(
element,
'requestTime',
TYPES_URN),
user_name=findtext(
element,
'userName',
TYPES_URN),
number_of_steps=findtext(
element,
'numberOfSteps',
TYPES_URN),
step_name=findtext(
element,
'step/name',
TYPES_URN),
step_number=findtext(
element,
'step_number',
TYPES_URN),
step_percent_complete=findtext(
element,
'step/percentComplete',
TYPES_URN),
failure_reason=findtext(
element,
'failureReason',
TYPES_URN))
return s