4818 lines
160 KiB
Python
4818 lines
160 KiB
Python
# 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.
|
|
|
|
from __future__ import with_statement
|
|
|
|
import base64
|
|
import warnings
|
|
|
|
from libcloud.utils.py3 import b
|
|
from libcloud.utils.py3 import urlparse
|
|
|
|
from libcloud.compute.providers import Provider
|
|
from libcloud.common.cloudstack import CloudStackDriverMixIn
|
|
from libcloud.compute.base import Node, NodeDriver, NodeImage, NodeLocation
|
|
from libcloud.compute.base import NodeSize, StorageVolume, VolumeSnapshot
|
|
from libcloud.compute.base import KeyPair
|
|
from libcloud.compute.types import NodeState, LibcloudError
|
|
from libcloud.compute.types import KeyPairDoesNotExistError, StorageVolumeState
|
|
from libcloud.utils.networking import is_private_subnet
|
|
|
|
|
|
# Utility functions
|
|
def transform_int_or_unlimited(value):
|
|
try:
|
|
return int(value)
|
|
except ValueError as e:
|
|
|
|
if str(value).lower() == 'unlimited':
|
|
return -1
|
|
|
|
raise e
|
|
|
|
|
|
"""
|
|
Define the extra dictionary for specific resources
|
|
"""
|
|
RESOURCE_EXTRA_ATTRIBUTES_MAP = {
|
|
'network': {
|
|
'broadcast_domain_type': {
|
|
'key_name': 'broadcastdomaintype',
|
|
'transform_func': str
|
|
},
|
|
'traffic_type': {
|
|
'key_name': 'traffictype',
|
|
'transform_func': str
|
|
},
|
|
'zone_name': {
|
|
'key_name': 'zonename',
|
|
'transform_func': str
|
|
},
|
|
'network_offering_name': {
|
|
'key_name': 'networkofferingname',
|
|
'transform_func': str
|
|
},
|
|
'network_offeringdisplay_text': {
|
|
'key_name': 'networkofferingdisplaytext',
|
|
'transform_func': str
|
|
},
|
|
'network_offering_availability': {
|
|
'key_name': 'networkofferingavailability',
|
|
'transform_func': str
|
|
},
|
|
'is_system': {
|
|
'key_name': 'issystem',
|
|
'transform_func': str
|
|
},
|
|
'state': {
|
|
'key_name': 'state',
|
|
'transform_func': str
|
|
},
|
|
'dns1': {
|
|
'key_name': 'dns1',
|
|
'transform_func': str
|
|
},
|
|
'dns2': {
|
|
'key_name': 'dns2',
|
|
'transform_func': str
|
|
},
|
|
'type': {
|
|
'key_name': 'type',
|
|
'transform_func': str
|
|
},
|
|
'acl_type': {
|
|
'key_name': 'acltype',
|
|
'transform_func': str
|
|
},
|
|
'subdomain_access': {
|
|
'key_name': 'subdomainaccess',
|
|
'transform_func': str
|
|
},
|
|
'network_domain': {
|
|
'key_name': 'networkdomain',
|
|
'transform_func': str
|
|
},
|
|
'physical_network_id': {
|
|
'key_name': 'physicalnetworkid',
|
|
'transform_func': str
|
|
},
|
|
'can_use_for_deploy': {
|
|
'key_name': 'canusefordeploy',
|
|
'transform_func': str
|
|
},
|
|
'gateway': {
|
|
'key_name': 'gateway',
|
|
'transform_func': str
|
|
},
|
|
'netmask': {
|
|
'key_name': 'netmask',
|
|
'transform_func': str
|
|
},
|
|
'vpc_id': {
|
|
'key_name': 'vpcid',
|
|
'transform_func': str
|
|
},
|
|
'project_id': {
|
|
'key_name': 'projectid',
|
|
'transform_func': str
|
|
}
|
|
},
|
|
'node': {
|
|
'haenable': {
|
|
'key_name': 'haenable',
|
|
'transform_func': str
|
|
},
|
|
'zone_id': {
|
|
'key_name': 'zoneid',
|
|
'transform_func': str
|
|
},
|
|
'zone_name': {
|
|
'key_name': 'zonename',
|
|
'transform_func': str
|
|
},
|
|
'key_name': {
|
|
'key_name': 'keypair',
|
|
'transform_func': str
|
|
},
|
|
'password': {
|
|
'key_name': 'password',
|
|
'transform_func': str
|
|
},
|
|
'image_id': {
|
|
'key_name': 'templateid',
|
|
'transform_func': str
|
|
},
|
|
'image_name': {
|
|
'key_name': 'templatename',
|
|
'transform_func': str
|
|
},
|
|
'template_display_text': {
|
|
'key_name': 'templatdisplaytext',
|
|
'transform_func': str
|
|
},
|
|
'password_enabled': {
|
|
'key_name': 'passwordenabled',
|
|
'transform_func': str
|
|
},
|
|
'size_id': {
|
|
'key_name': 'serviceofferingid',
|
|
'transform_func': str
|
|
},
|
|
'size_name': {
|
|
'key_name': 'serviceofferingname',
|
|
'transform_func': str
|
|
},
|
|
'root_device_id': {
|
|
'key_name': 'rootdeviceid',
|
|
'transform_func': str
|
|
},
|
|
'root_device_type': {
|
|
'key_name': 'rootdevicetype',
|
|
'transform_func': str
|
|
},
|
|
'hypervisor': {
|
|
'key_name': 'hypervisor',
|
|
'transform_func': str
|
|
},
|
|
'project': {
|
|
'key_name': 'project',
|
|
'transform_func': str
|
|
},
|
|
'project_id': {
|
|
'key_name': 'projectid',
|
|
'transform_func': str
|
|
},
|
|
'nics:': {
|
|
'key_name': 'nic',
|
|
'transform_func': list
|
|
}
|
|
},
|
|
'volume': {
|
|
'created': {
|
|
'key_name': 'created',
|
|
'transform_func': str
|
|
},
|
|
'device_id': {
|
|
'key_name': 'deviceid',
|
|
'transform_func': transform_int_or_unlimited
|
|
},
|
|
'instance_id': {
|
|
'key_name': 'virtualmachineid',
|
|
'transform_func': str
|
|
},
|
|
'serviceoffering_id': {
|
|
'key_name': 'serviceofferingid',
|
|
'transform_func': str
|
|
},
|
|
'state': {
|
|
'key_name': 'state',
|
|
'transform_func': str
|
|
},
|
|
'volume_type': {
|
|
'key_name': 'type',
|
|
'transform_func': str
|
|
},
|
|
'zone_id': {
|
|
'key_name': 'zoneid',
|
|
'transform_func': str
|
|
},
|
|
'zone_name': {
|
|
'key_name': 'zonename',
|
|
'transform_func': str
|
|
}
|
|
},
|
|
'vpc': {
|
|
'created': {
|
|
'key_name': 'created',
|
|
'transform_func': str
|
|
},
|
|
'domain': {
|
|
'key_name': 'domain',
|
|
'transform_func': str
|
|
},
|
|
'domain_id': {
|
|
'key_name': 'domainid',
|
|
'transform_func': transform_int_or_unlimited
|
|
},
|
|
'network_domain': {
|
|
'key_name': 'networkdomain',
|
|
'transform_func': str
|
|
},
|
|
'state': {
|
|
'key_name': 'state',
|
|
'transform_func': str
|
|
},
|
|
'vpc_offering_id': {
|
|
'key_name': 'vpcofferingid',
|
|
'transform_func': str
|
|
},
|
|
'zone_name': {
|
|
'key_name': 'zonename',
|
|
'transform_func': str
|
|
},
|
|
'zone_id': {
|
|
'key_name': 'zoneid',
|
|
'transform_func': str
|
|
}
|
|
},
|
|
'project': {
|
|
'account': {'key_name': 'account', 'transform_func': str},
|
|
'cpuavailable': {'key_name': 'cpuavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'cpulimit': {'key_name': 'cpulimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'cputotal': {'key_name': 'cputotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'domain': {'key_name': 'domain', 'transform_func': str},
|
|
'domainid': {'key_name': 'domainid', 'transform_func': str},
|
|
'ipavailable': {'key_name': 'ipavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'iplimit': {'key_name': 'iplimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'iptotal': {'key_name': 'iptotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'memoryavailable': {'key_name': 'memoryavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'memorylimit': {'key_name': 'memorylimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'memorytotal': {'key_name': 'memorytotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'networkavailable': {'key_name': 'networkavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'networklimit': {'key_name': 'networklimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'networktotal': {'key_name': 'networktotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'primarystorageavailable': {
|
|
'key_name': 'primarystorageavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'primarystoragelimit': {'key_name': 'primarystoragelimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'primarystoragetotal': {'key_name': 'primarystoragetotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'secondarystorageavailable': {
|
|
'key_name': 'secondarystorageavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'secondarystoragelimit': {
|
|
'key_name': 'secondarystoragelimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'secondarystoragetotal': {
|
|
'key_name': 'secondarystoragetotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'snapshotavailable': {'key_name': 'snapshotavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'snapshotlimit': {'key_name': 'snapshotlimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'snapshottotal': {'key_name': 'snapshottotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'state': {'key_name': 'state', 'transform_func': str},
|
|
'tags': {'key_name': 'tags', 'transform_func': str},
|
|
'templateavailable': {'key_name': 'templateavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'templatelimit': {'key_name': 'templatelimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'templatetotal': {'key_name': 'templatetotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vmavailable': {'key_name': 'vmavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vmlimit': {'key_name': 'vmlimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vmrunning': {'key_name': 'vmrunning',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vmtotal': {'key_name': 'vmtotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'volumeavailable': {'key_name': 'volumeavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'volumelimit': {'key_name': 'volumelimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'volumetotal': {'key_name': 'volumetotal',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vpcavailable': {'key_name': 'vpcavailable',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vpclimit': {'key_name': 'vpclimit',
|
|
'transform_func': transform_int_or_unlimited},
|
|
'vpctotal': {'key_name': 'vpctotal',
|
|
'transform_func': transform_int_or_unlimited}
|
|
},
|
|
'nic': {
|
|
'secondary_ip': {
|
|
'key_name': 'secondaryip',
|
|
'transform_func': list
|
|
}
|
|
},
|
|
'vpngateway': {
|
|
'for_display': {
|
|
'key_name': 'fordisplay',
|
|
'transform_func': str
|
|
},
|
|
'project': {
|
|
'key_name': 'project',
|
|
'transform_func': str
|
|
},
|
|
'project_id': {
|
|
'key_name': 'projectid',
|
|
'transform_func': str
|
|
},
|
|
'removed': {
|
|
'key_name': 'removed',
|
|
'transform_func': str
|
|
}
|
|
},
|
|
'vpncustomergateway': {
|
|
'account': {
|
|
'key_name': 'account',
|
|
'transform_func': str
|
|
},
|
|
'domain': {
|
|
'key_name': 'domain',
|
|
'transform_func': str
|
|
},
|
|
'domain_id': {
|
|
'key_name': 'domainid',
|
|
'transform_func': str
|
|
},
|
|
'dpd': {
|
|
'key_name': 'dpd',
|
|
'transform_func': bool
|
|
},
|
|
'esp_lifetime': {
|
|
'key_name': 'esplifetime',
|
|
'transform_func': transform_int_or_unlimited
|
|
},
|
|
'ike_lifetime': {
|
|
'key_name': 'ikelifetime',
|
|
'transform_func': transform_int_or_unlimited
|
|
},
|
|
'name': {
|
|
'key_name': 'name',
|
|
'transform_func': str
|
|
}
|
|
},
|
|
'vpnconnection': {
|
|
'account': {
|
|
'key_name': 'account',
|
|
'transform_func': str
|
|
},
|
|
'domain': {
|
|
'key_name': 'domain',
|
|
'transform_func': str
|
|
},
|
|
'domain_id': {
|
|
'key_name': 'domainid',
|
|
'transform_func': str
|
|
},
|
|
'for_display': {
|
|
'key_name': 'fordisplay',
|
|
'transform_func': str
|
|
},
|
|
'project': {
|
|
'key_name': 'project',
|
|
'transform_func': str
|
|
},
|
|
'project_id': {
|
|
'key_name': 'projectid',
|
|
'transform_func': str
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class CloudStackNode(Node):
|
|
"""
|
|
Subclass of Node so we can expose our extension methods.
|
|
"""
|
|
|
|
def ex_allocate_public_ip(self):
|
|
"""
|
|
Allocate a public IP and bind it to this node.
|
|
"""
|
|
return self.driver.ex_allocate_public_ip(self)
|
|
|
|
def ex_release_public_ip(self, address):
|
|
"""
|
|
Release a public IP that this node holds.
|
|
"""
|
|
return self.driver.ex_release_public_ip(self, address)
|
|
|
|
def ex_create_ip_forwarding_rule(self, address, protocol,
|
|
start_port, end_port=None):
|
|
"""
|
|
Add a NAT/firewall forwarding rule for a port or ports.
|
|
"""
|
|
return self.driver.ex_create_ip_forwarding_rule(node=self,
|
|
address=address,
|
|
protocol=protocol,
|
|
start_port=start_port,
|
|
end_port=end_port)
|
|
|
|
def ex_create_port_forwarding_rule(self, address,
|
|
private_port, public_port,
|
|
protocol,
|
|
public_end_port=None,
|
|
private_end_port=None,
|
|
openfirewall=True):
|
|
"""
|
|
Add a port forwarding rule for port or ports.
|
|
"""
|
|
return self.driver.ex_create_port_forwarding_rule(
|
|
node=self, address=address, private_port=private_port,
|
|
public_port=public_port, protocol=protocol,
|
|
public_end_port=public_end_port, private_end_port=private_end_port,
|
|
openfirewall=openfirewall)
|
|
|
|
def ex_delete_ip_forwarding_rule(self, rule):
|
|
"""
|
|
Delete a port forwarding rule.
|
|
"""
|
|
return self.driver.ex_delete_ip_forwarding_rule(node=self, rule=rule)
|
|
|
|
def ex_delete_port_forwarding_rule(self, rule):
|
|
"""
|
|
Delete a NAT/firewall rule.
|
|
"""
|
|
return self.driver.ex_delete_port_forwarding_rule(node=self, rule=rule)
|
|
|
|
def ex_restore(self, template=None):
|
|
"""
|
|
Restore virtual machine
|
|
"""
|
|
return self.driver.ex_restore(node=self, template=template)
|
|
|
|
def ex_change_node_size(self, offering):
|
|
"""
|
|
Change virtual machine offering/size
|
|
"""
|
|
return self.driver.ex_change_node_size(node=self, offering=offering)
|
|
|
|
def ex_start(self):
|
|
"""
|
|
Starts a stopped virtual machine.
|
|
"""
|
|
return self.driver.ex_start(node=self)
|
|
|
|
def ex_stop(self):
|
|
"""
|
|
Stops a running virtual machine.
|
|
"""
|
|
return self.driver.ex_stop(node=self)
|
|
|
|
|
|
class CloudStackAddress(object):
|
|
"""
|
|
A public IP address.
|
|
|
|
:param id: UUID of the Public IP
|
|
:type id: ``str``
|
|
|
|
:param address: The public IP address
|
|
:type address: ``str``
|
|
|
|
:param associated_network_id: The ID of the network where this address
|
|
has been associated with
|
|
:type associated_network_id: ``str``
|
|
|
|
:param vpc_id: VPC the ip belongs to
|
|
:type vpc_id: ``str``
|
|
|
|
:param virtualmachine_id: The ID of virutal machine this address
|
|
is assigned to
|
|
:type virtualmachine_id: ``str``
|
|
"""
|
|
|
|
def __init__(self, id, address, driver, associated_network_id=None,
|
|
vpc_id=None, virtualmachine_id=None):
|
|
self.id = id
|
|
self.address = address
|
|
self.driver = driver
|
|
self.associated_network_id = associated_network_id
|
|
self.vpc_id = vpc_id
|
|
self.virtualmachine_id = virtualmachine_id
|
|
|
|
def release(self):
|
|
self.driver.ex_release_public_ip(address=self)
|
|
|
|
def __str__(self):
|
|
return self.address
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackFirewallRule(object):
|
|
"""
|
|
A firewall rule.
|
|
"""
|
|
|
|
def __init__(self, id, address, cidr_list, protocol,
|
|
icmp_code=None, icmp_type=None,
|
|
start_port=None, end_port=None):
|
|
|
|
"""
|
|
A Firewall rule.
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
CloudStack.
|
|
|
|
:param id: Firewall Rule ID
|
|
:type id: ``int``
|
|
|
|
:param address: External IP address
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param cidr_list: cidr list
|
|
:type cidr_list: ``str``
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param icmp_code: Error code for this icmp message
|
|
:type icmp_code: ``int``
|
|
|
|
:param icmp_type: Type of the icmp message being sent
|
|
:type icmp_type: ``int``
|
|
|
|
:param start_port: start of port range
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: end of port range
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackFirewallRule`
|
|
"""
|
|
|
|
self.id = id
|
|
self.address = address
|
|
self.cidr_list = cidr_list
|
|
self.protocol = protocol
|
|
self.icmp_code = icmp_code
|
|
self.icmp_type = icmp_type
|
|
self.start_port = start_port
|
|
self.end_port = end_port
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackEgressFirewallRule(object):
|
|
"""
|
|
A egress firewall rule.
|
|
"""
|
|
|
|
def __init__(self, id, network_id, cidr_list, protocol,
|
|
icmp_code=None, icmp_type=None,
|
|
start_port=None, end_port=None):
|
|
|
|
"""
|
|
A egress firewall rule.
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
CloudStack.
|
|
|
|
:param id: Firewall Rule ID
|
|
:type id: ``int``
|
|
|
|
:param network_id: the id network network for the egress firwall
|
|
services
|
|
:type network_id: ``str``
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param cidr_list: cidr list
|
|
:type cidr_list: ``str``
|
|
|
|
:param icmp_code: Error code for this icmp message
|
|
:type icmp_code: ``int``
|
|
|
|
:param icmp_type: Type of the icmp message being sent
|
|
:type icmp_type: ``int``
|
|
|
|
:param start_port: start of port range
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: end of port range
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackEgressFirewallRule`
|
|
"""
|
|
|
|
self.id = id
|
|
self.network_id = network_id
|
|
self.cidr_list = cidr_list
|
|
self.protocol = protocol
|
|
self.icmp_code = icmp_code
|
|
self.icmp_type = icmp_type
|
|
self.start_port = start_port
|
|
self.end_port = end_port
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackIPForwardingRule(object):
|
|
"""
|
|
A NAT/firewall forwarding rule.
|
|
"""
|
|
|
|
def __init__(self, node, id, address, protocol, start_port, end_port=None):
|
|
"""
|
|
A NAT/firewall forwarding rule.
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
CloudStack.
|
|
|
|
:param node: Node for rule
|
|
:type node: :class:`Node`
|
|
|
|
:param id: Rule ID
|
|
:type id: ``int``
|
|
|
|
:param address: External IP address
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param start_port: Start port for the rule
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: End port for the rule
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackIPForwardingRule`
|
|
"""
|
|
self.node = node
|
|
self.id = id
|
|
self.address = address
|
|
self.protocol = protocol
|
|
self.start_port = start_port
|
|
self.end_port = end_port
|
|
|
|
def delete(self):
|
|
self.node.ex_delete_ip_forwarding_rule(rule=self)
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackPortForwardingRule(object):
|
|
"""
|
|
A Port forwarding rule for Source NAT.
|
|
"""
|
|
|
|
def __init__(self, node, rule_id, address, protocol, public_port,
|
|
private_port, public_end_port=None, private_end_port=None,
|
|
network_id=None):
|
|
"""
|
|
A Port forwarding rule for Source NAT.
|
|
|
|
@note: This is a non-standard extension API, and only works for EC2.
|
|
|
|
:param node: Node for rule
|
|
:type node: :class:`Node`
|
|
|
|
:param rule_id: Rule ID
|
|
:type rule_id: ``int``
|
|
|
|
:param address: External IP address
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param public_port: External port for rule (or start port if
|
|
public_end_port is also provided)
|
|
:type public_port: ``int``
|
|
|
|
:param private_port: Internal node port for rule (or start port if
|
|
public_end_port is also provided)
|
|
:type private_port: ``int``
|
|
|
|
:param public_end_port: End of external port range
|
|
:type public_end_port: ``int``
|
|
|
|
:param private_end_port: End of internal port range
|
|
:type private_end_port: ``int``
|
|
|
|
:param network_id: The network of the vm the Port Forwarding rule
|
|
will be created for. Required when public Ip
|
|
address is not associated with any Guest
|
|
network yet (VPC case)
|
|
:type network_id: ``str``
|
|
|
|
:rtype: :class:`CloudStackPortForwardingRule`
|
|
"""
|
|
self.node = node
|
|
self.id = rule_id
|
|
self.address = address
|
|
self.protocol = protocol
|
|
self.public_port = public_port
|
|
self.public_end_port = public_end_port
|
|
self.private_port = private_port
|
|
self.private_end_port = private_end_port
|
|
|
|
def delete(self):
|
|
self.node.ex_delete_port_forwarding_rule(rule=self)
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackNetworkACLList(object):
|
|
"""
|
|
a Network ACL for the given VPC
|
|
"""
|
|
|
|
def __init__(self, acl_id, name, vpc_id, driver, description=None):
|
|
"""
|
|
a Network ACL for the given VPC
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
Cloudstack.
|
|
|
|
:param acl_id: ACL ID
|
|
:type acl_id: ``int``
|
|
|
|
:param name: Name of the network ACL List
|
|
:type name: ``str``
|
|
|
|
:param vpc_id: Id of the VPC associated with this network ACL List
|
|
:type vpc_id: ``string``
|
|
|
|
:param description: Description of the network ACL List
|
|
:type description: ``str``
|
|
|
|
:rtype: :class:`CloudStackNetworkACLList`
|
|
"""
|
|
|
|
self.id = acl_id
|
|
self.name = name
|
|
self.vpc_id = vpc_id
|
|
self.driver = driver
|
|
self.description = description
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackNetworkACLList: id=%s, name=%s, vpc_id=%s, '
|
|
'driver=%s, description=%s>')
|
|
% (self.id, self.name, self.vpc_id,
|
|
self.driver.name, self.description))
|
|
|
|
|
|
class CloudStackNetworkACL(object):
|
|
"""
|
|
a ACL rule in the given network (the network has to belong to VPC)
|
|
"""
|
|
|
|
def __init__(self, id, protocol, acl_id, action, cidr_list,
|
|
start_port, end_port, traffic_type=None):
|
|
"""
|
|
a ACL rule in the given network (the network has to belong to
|
|
VPC)
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
Cloudstack.
|
|
|
|
:param id: the ID of the ACL Item
|
|
:type id ``int``
|
|
|
|
:param protocol: the protocol for the ACL rule. Valid values are
|
|
TCP/UDP/ICMP/ALL or valid protocol number
|
|
:type protocol: ``string``
|
|
|
|
:param acl_id: Name of the network ACL List
|
|
:type acl_id: ``str``
|
|
|
|
:param action: scl entry action, allow or deny
|
|
:type action: ``string``
|
|
|
|
:param cidr_list: the cidr list to allow traffic from/to
|
|
:type cidr_list: ``str``
|
|
|
|
:param start_port: the starting port of ACL
|
|
:type start_port: ``str``
|
|
|
|
:param end_port: the ending port of ACL
|
|
:type end_port: ``str``
|
|
|
|
:param traffic_type: the traffic type for the ACL,can be Ingress
|
|
or Egress, defaulted to Ingress if not
|
|
specified
|
|
:type traffic_type: ``str``
|
|
|
|
:rtype: :class:`CloudStackNetworkACL`
|
|
"""
|
|
|
|
self.id = id
|
|
self.protocol = protocol
|
|
self.acl_id = acl_id
|
|
self.action = action
|
|
self.cidr_list = cidr_list
|
|
self.start_port = start_port
|
|
self.end_port = end_port
|
|
self.traffic_type = traffic_type
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackDiskOffering(object):
|
|
"""
|
|
A disk offering within CloudStack.
|
|
"""
|
|
|
|
def __init__(self, id, name, size, customizable):
|
|
self.id = id
|
|
self.name = name
|
|
self.size = size
|
|
self.customizable = customizable
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackNetwork(object):
|
|
"""
|
|
Class representing a CloudStack Network.
|
|
"""
|
|
|
|
def __init__(self, displaytext, name, networkofferingid, id, zoneid,
|
|
driver, extra=None):
|
|
self.displaytext = displaytext
|
|
self.name = name
|
|
self.networkofferingid = networkofferingid
|
|
self.id = id
|
|
self.zoneid = zoneid
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackNetwork: displaytext=%s, name=%s, '
|
|
'networkofferingid=%s, '
|
|
'id=%s, zoneid=%s, driver=%s>')
|
|
% (self.displaytext, self.name, self.networkofferingid,
|
|
self.id, self.zoneid, self.driver.name))
|
|
|
|
|
|
class CloudStackNetworkOffering(object):
|
|
"""
|
|
Class representing a CloudStack Network Offering.
|
|
"""
|
|
|
|
def __init__(self, name, display_text, guest_ip_type, id,
|
|
service_offering_id, for_vpc, driver, extra=None):
|
|
self.display_text = display_text
|
|
self.name = name
|
|
self.guest_ip_type = guest_ip_type
|
|
self.id = id
|
|
self.service_offering_id = service_offering_id
|
|
self.for_vpc = for_vpc
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackNetworkOffering: id=%s, name=%s, '
|
|
'display_text=%s, guest_ip_type=%s, service_offering_id=%s, '
|
|
'for_vpc=%s, driver=%s>')
|
|
% (self.id, self.name, self.display_text,
|
|
self.guest_ip_type, self.service_offering_id, self.for_vpc,
|
|
self.driver.name))
|
|
|
|
|
|
class CloudStackNic(object):
|
|
"""
|
|
Class representing a CloudStack Network Interface.
|
|
"""
|
|
|
|
def __init__(self, id, network_id, net_mask, gateway, ip_address,
|
|
is_default, mac_address, driver, extra=None):
|
|
self.id = id
|
|
self.network_id = network_id
|
|
self.net_mask = net_mask
|
|
self.gateway = gateway
|
|
self.ip_address = ip_address
|
|
self.is_default = is_default
|
|
self.mac_address = mac_address
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackNic: id=%s, network_id=%s, '
|
|
'net_mask=%s, gateway=%s, ip_address=%s, '
|
|
'is_default=%s, mac_address=%s, driver%s>')
|
|
% (self.id, self.network_id, self.net_mask,
|
|
self.gateway, self.ip_address, self.is_default,
|
|
self.mac_address, self.driver.name))
|
|
|
|
def __eq__(self, other):
|
|
return self.__class__ is other.__class__ and self.id == other.id
|
|
|
|
|
|
class CloudStackVPC(object):
|
|
"""
|
|
Class representing a CloudStack VPC.
|
|
"""
|
|
|
|
def __init__(self, name, vpc_offering_id, id, cidr, driver,
|
|
zone_id=None, display_text=None, extra=None):
|
|
self.display_text = display_text
|
|
self.name = name
|
|
self.vpc_offering_id = vpc_offering_id
|
|
self.id = id
|
|
self.zone_id = zone_id
|
|
self.cidr = cidr
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackVPC: name=%s, vpc_offering_id=%s, id=%s, '
|
|
'cidr=%s, driver=%s, zone_id=%s, display_text=%s>')
|
|
% (self.name, self.vpc_offering_id, self.id,
|
|
self.cidr, self.driver.name, self.zone_id,
|
|
self.display_text))
|
|
|
|
|
|
class CloudStackVPCOffering(object):
|
|
"""
|
|
Class representing a CloudStack VPC Offering.
|
|
"""
|
|
|
|
def __init__(self, name, display_text, id,
|
|
driver, extra=None):
|
|
self.name = name
|
|
self.display_text = display_text
|
|
self.id = id
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackVPCOffering: name=%s, display_text=%s, '
|
|
'id=%s, '
|
|
'driver=%s>')
|
|
% (self.name, self.display_text, self.id,
|
|
self.driver.name))
|
|
|
|
|
|
class CloudStackVpnGateway(object):
|
|
"""
|
|
Class representing a CloudStack VPN Gateway.
|
|
"""
|
|
def __init__(self, id, account, domain, domain_id,
|
|
public_ip, vpc_id, driver, extra=None):
|
|
self.id = id
|
|
self.account = account
|
|
self.domain = domain
|
|
self.domain_id = domain_id
|
|
self.public_ip = public_ip
|
|
self.vpc_id = vpc_id
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
@property
|
|
def vpc(self):
|
|
for vpc in self.driver.ex_list_vpcs():
|
|
if self.vpc_id == vpc.id:
|
|
return vpc
|
|
|
|
raise LibcloudError('VPC with id=%s not found' % self.vpc_id)
|
|
|
|
def delete(self):
|
|
return self.driver.ex_delete_vpn_gateway(vpn_gateway=self)
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackVpnGateway: account=%s, domain=%s, '
|
|
'domain_id=%s, id=%s, public_ip=%s, vpc_id=%s, '
|
|
'driver=%s>')
|
|
% (self.account, self.domain, self.domain_id,
|
|
self.id, self.public_ip, self.vpc_id, self.driver.name))
|
|
|
|
|
|
class CloudStackVpnCustomerGateway(object):
|
|
"""
|
|
Class representing a CloudStack VPN Customer Gateway.
|
|
"""
|
|
def __init__(self, id, cidr_list, esp_policy, gateway,
|
|
ike_policy, ipsec_psk, driver, extra=None):
|
|
self.id = id
|
|
self.cidr_list = cidr_list
|
|
self.esp_policy = esp_policy
|
|
self.gateway = gateway
|
|
self.ike_policy = ike_policy
|
|
self.ipsec_psk = ipsec_psk
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def delete(self):
|
|
return self.driver.ex_delete_vpn_customer_gateway(
|
|
vpn_customer_gateway=self)
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackVpnCustomerGateway: id=%s, cidr_list=%s, '
|
|
'esp_policy=%s, gateway=%s, ike_policy=%s, ipsec_psk=%s, '
|
|
'driver=%s>')
|
|
% (self.id, self.cidr_list, self.esp_policy, self.gateway,
|
|
self.ike_policy, self.ipsec_psk, self.driver.name))
|
|
|
|
|
|
class CloudStackVpnConnection(object):
|
|
"""
|
|
Class representing a CloudStack VPN Connection.
|
|
"""
|
|
def __init__(self, id, passive, vpn_customer_gateway_id,
|
|
vpn_gateway_id, state, driver, extra=None):
|
|
self.id = id
|
|
self.passive = passive
|
|
self.vpn_customer_gateway_id = vpn_customer_gateway_id
|
|
self.vpn_gateway_id = vpn_gateway_id
|
|
self.state = state
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
@property
|
|
def vpn_customer_gateway(self):
|
|
try:
|
|
return self.driver.ex_list_vpn_customer_gateways(
|
|
id=self.vpn_customer_gateway_id)[0]
|
|
except IndexError:
|
|
raise LibcloudError('VPN Customer Gateway with id=%s not found' %
|
|
self.vpn_customer_gateway_id)
|
|
|
|
@property
|
|
def vpn_gateway(self):
|
|
try:
|
|
return self.driver.ex_list_vpn_gateways(id=self.vpn_gateway_id)[0]
|
|
except IndexError:
|
|
raise LibcloudError('VPN Gateway with id=%s not found' %
|
|
self.vpn_gateway_id)
|
|
|
|
def delete(self):
|
|
return self.driver.ex_delete_vpn_connection(vpn_connection=self)
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackVpnConnection: id=%s, passive=%s, '
|
|
'vpn_customer_gateway_id=%s, vpn_gateway_id=%s, state=%s, '
|
|
'driver=%s>')
|
|
% (self.id, self.passive, self.vpn_customer_gateway_id,
|
|
self.vpn_gateway_id, self.state, self.driver.name))
|
|
|
|
|
|
class CloudStackRouter(object):
|
|
"""
|
|
Class representing a CloudStack Router.
|
|
"""
|
|
|
|
def __init__(self, id, name, state, public_ip, vpc_id, driver):
|
|
self.id = id
|
|
self.name = name
|
|
self.state = state
|
|
self.public_ip = public_ip
|
|
self.vpc_id = vpc_id
|
|
self.driver = driver
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackRouter: id=%s, name=%s, state=%s, '
|
|
'public_ip=%s, vpc_id=%s, driver=%s>')
|
|
% (self.id, self.name, self.state,
|
|
self.public_ip, self.vpc_id, self.driver.name))
|
|
|
|
|
|
class CloudStackProject(object):
|
|
"""
|
|
Class representing a CloudStack Project.
|
|
"""
|
|
|
|
def __init__(self, id, name, display_text, driver, extra=None):
|
|
self.id = id
|
|
self.name = name
|
|
self.display_text = display_text
|
|
self.driver = driver
|
|
self.extra = extra or {}
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackProject: id=%s, name=%s, display_text=%s,'
|
|
'driver=%s>')
|
|
% (self.id, self.display_text, self.name,
|
|
self.driver.name))
|
|
|
|
|
|
class CloudStackAffinityGroup(object):
|
|
"""
|
|
Class representing a CloudStack AffinityGroup.
|
|
"""
|
|
|
|
def __init__(self, id, account, description, domain, domainid, name,
|
|
group_type, virtualmachine_ids):
|
|
"""
|
|
A CloudStack Affinity Group.
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
CloudStack.
|
|
|
|
:param id: CloudStack Affinity Group ID
|
|
:type id: ``str``
|
|
|
|
:param account: An account for the affinity group. Must be used
|
|
with domainId.
|
|
:type account: ``str``
|
|
|
|
:param description: optional description of the affinity group
|
|
:type description: ``str``
|
|
|
|
:param domain: the domain name of the affinity group
|
|
:type domain: ``str``
|
|
|
|
:param domainid: domain ID of the account owning the affinity
|
|
group
|
|
:type domainid: ``str``
|
|
|
|
:param name: name of the affinity group
|
|
:type name: ``str``
|
|
|
|
:param group_type: the type of the affinity group
|
|
:type group_type: :class:`CloudStackAffinityGroupType`
|
|
|
|
:param virtualmachine_ids: virtual machine Ids associated with
|
|
this affinity group
|
|
:type virtualmachine_ids: ``str``
|
|
|
|
:rtype: :class:`CloudStackAffinityGroup`
|
|
"""
|
|
self.id = id
|
|
self.account = account
|
|
self.description = description
|
|
self.domain = domain
|
|
self.domainid = domainid
|
|
self.name = name
|
|
self.type = group_type
|
|
self.virtualmachine_ids = virtualmachine_ids
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackAffinityGroup: id=%s, name=%s, type=%s>')
|
|
% (self.id, self.name, self.type))
|
|
|
|
|
|
class CloudStackAffinityGroupType(object):
|
|
"""
|
|
Class representing a CloudStack AffinityGroupType.
|
|
"""
|
|
|
|
def __init__(self, type_name):
|
|
"""
|
|
A CloudStack Affinity Group Type.
|
|
|
|
@note: This is a non-standard extension API, and only works for
|
|
CloudStack.
|
|
|
|
:param type_name: the type of the affinity group
|
|
:type type_name: ``str``
|
|
|
|
:rtype: :class:`CloudStackAffinityGroupType`
|
|
"""
|
|
self.type = type_name
|
|
|
|
def __repr__(self):
|
|
return (('<CloudStackAffinityGroupType: type=%s>') % self.type)
|
|
|
|
|
|
class CloudStackNodeDriver(CloudStackDriverMixIn, NodeDriver):
|
|
"""
|
|
Driver for the CloudStack API.
|
|
|
|
:cvar host: The host where the API can be reached.
|
|
:cvar path: The path where the API can be reached.
|
|
:cvar async_poll_frequency: How often (in seconds) to poll for async
|
|
job completion.
|
|
:type async_poll_frequency: ``int``"""
|
|
|
|
name = 'CloudStack'
|
|
api_name = 'cloudstack'
|
|
website = 'http://cloudstack.org/'
|
|
type = Provider.CLOUDSTACK
|
|
|
|
features = {'create_node': ['generates_password']}
|
|
|
|
NODE_STATE_MAP = {
|
|
'Running': NodeState.RUNNING,
|
|
'Starting': NodeState.REBOOTING,
|
|
'Migrating': NodeState.MIGRATING,
|
|
'Stopped': NodeState.STOPPED,
|
|
'Stopping': NodeState.PENDING,
|
|
'Destroyed': NodeState.TERMINATED,
|
|
'Expunging': NodeState.PENDING,
|
|
'Error': NodeState.TERMINATED
|
|
}
|
|
|
|
VOLUME_STATE_MAP = {
|
|
'Creating': StorageVolumeState.CREATING,
|
|
'Destroying': StorageVolumeState.DELETING,
|
|
'Expunging': StorageVolumeState.DELETING,
|
|
'Destroy': StorageVolumeState.DELETED,
|
|
'Expunged': StorageVolumeState.DELETED,
|
|
'Allocated': StorageVolumeState.AVAILABLE,
|
|
'Ready': StorageVolumeState.AVAILABLE,
|
|
'Snapshotting': StorageVolumeState.BACKUP,
|
|
'UploadError': StorageVolumeState.ERROR,
|
|
'Migrating': StorageVolumeState.MIGRATING
|
|
}
|
|
|
|
def __init__(self, key, secret=None, secure=True, host=None,
|
|
path=None, port=None, url=None, *args, **kwargs):
|
|
"""
|
|
:inherits: :class:`NodeDriver.__init__`
|
|
|
|
:param host: The host where the API can be reached. (required)
|
|
:type host: ``str``
|
|
|
|
:param path: The path where the API can be reached. (required)
|
|
:type path: ``str``
|
|
|
|
:param url: Full URL to the API endpoint. Mutually exclusive with host
|
|
and path argument.
|
|
:type url: ``str``
|
|
"""
|
|
if url:
|
|
parsed = urlparse.urlparse(url)
|
|
|
|
path = parsed.path
|
|
|
|
scheme = parsed.scheme
|
|
split = parsed.netloc.split(':')
|
|
|
|
if len(split) == 1:
|
|
# No port provided, use the default one
|
|
host = parsed.netloc
|
|
port = 443 if scheme == 'https' else 80
|
|
else:
|
|
host = split[0]
|
|
port = int(split[1])
|
|
else:
|
|
host = host if host else self.host
|
|
path = path if path else self.path
|
|
|
|
if path is not None:
|
|
self.path = path
|
|
|
|
if host is not None:
|
|
self.host = host
|
|
|
|
if (self.type == Provider.CLOUDSTACK) and (not host or not path):
|
|
raise Exception('When instantiating CloudStack driver directly '
|
|
'you also need to provide url or host and path '
|
|
'argument')
|
|
|
|
super(CloudStackNodeDriver, self).__init__(key=key,
|
|
secret=secret,
|
|
secure=secure,
|
|
host=host,
|
|
port=port)
|
|
|
|
def list_images(self, location=None):
|
|
args = {
|
|
'templatefilter': 'executable'
|
|
}
|
|
if location is not None:
|
|
args['zoneid'] = location.id
|
|
|
|
imgs = self._sync_request(command='listTemplates',
|
|
params=args,
|
|
method='GET')
|
|
images = []
|
|
for img in imgs.get('template', []):
|
|
|
|
extra = {'hypervisor': img['hypervisor'],
|
|
'format': img['format'],
|
|
'os': img['ostypename'],
|
|
'displaytext': img['displaytext']}
|
|
|
|
size = img.get('size', None)
|
|
if size is not None:
|
|
extra.update({'size': img['size']})
|
|
|
|
images.append(NodeImage(
|
|
id=img['id'],
|
|
name=img['name'],
|
|
driver=self.connection.driver,
|
|
extra=extra))
|
|
return images
|
|
|
|
def list_locations(self):
|
|
"""
|
|
:rtype ``list`` of :class:`NodeLocation`
|
|
"""
|
|
locs = self._sync_request('listZones')
|
|
|
|
locations = []
|
|
for loc in locs['zone']:
|
|
location = NodeLocation(str(loc['id']), loc['name'], 'Unknown',
|
|
self)
|
|
locations.append(location)
|
|
|
|
return locations
|
|
|
|
def list_nodes(self, project=None, location=None):
|
|
"""
|
|
@inherits: :class:`NodeDriver.list_nodes`
|
|
|
|
:keyword project: Limit nodes returned to those configured under
|
|
the defined project.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:keyword location: Limit nodes returned to those in the defined
|
|
location.
|
|
:type location: :class:`.NodeLocation`
|
|
|
|
:rtype: ``list`` of :class:`CloudStackNode`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if project:
|
|
args['projectid'] = project.id
|
|
|
|
if location is not None:
|
|
args['zoneid'] = location.id
|
|
|
|
vms = self._sync_request('listVirtualMachines', params=args)
|
|
addrs = self._sync_request('listPublicIpAddresses', params=args)
|
|
port_forwarding_rules = self._sync_request('listPortForwardingRules')
|
|
ip_forwarding_rules = self._sync_request('listIpForwardingRules')
|
|
|
|
public_ips_map = {}
|
|
for addr in addrs.get('publicipaddress', []):
|
|
if 'virtualmachineid' not in addr:
|
|
continue
|
|
vm_id = str(addr['virtualmachineid'])
|
|
if vm_id not in public_ips_map:
|
|
public_ips_map[vm_id] = {}
|
|
public_ips_map[vm_id][addr['ipaddress']] = addr['id']
|
|
|
|
nodes = []
|
|
|
|
for vm in vms.get('virtualmachine', []):
|
|
public_ips = public_ips_map.get(str(vm['id']), {}).keys()
|
|
public_ips = list(public_ips)
|
|
node = self._to_node(data=vm, public_ips=public_ips)
|
|
|
|
addresses = public_ips_map.get(str(vm['id']), {}).items()
|
|
addresses = [CloudStackAddress(id=address_id, address=address,
|
|
driver=node.driver) for
|
|
address, address_id in addresses]
|
|
node.extra['ip_addresses'] = addresses
|
|
|
|
rules = []
|
|
for addr in addresses:
|
|
for r in ip_forwarding_rules.get('ipforwardingrule', []):
|
|
if str(r['virtualmachineid']) == node.id:
|
|
rule = CloudStackIPForwardingRule(node, r['id'],
|
|
addr,
|
|
r['protocol']
|
|
.upper(),
|
|
r['startport'],
|
|
r['endport'])
|
|
rules.append(rule)
|
|
node.extra['ip_forwarding_rules'] = rules
|
|
|
|
rules = []
|
|
for r in port_forwarding_rules.get('portforwardingrule', []):
|
|
if str(r['virtualmachineid']) == node.id:
|
|
addr = [CloudStackAddress(id=a['id'],
|
|
address=a['ipaddress'],
|
|
driver=node.driver)
|
|
for a in addrs.get('publicipaddress', [])
|
|
if a['ipaddress'] == r['ipaddress']]
|
|
rule = CloudStackPortForwardingRule(node, r['id'],
|
|
addr[0],
|
|
r['protocol'].upper(),
|
|
r['publicport'],
|
|
r['privateport'],
|
|
r['publicendport'],
|
|
r['privateendport'])
|
|
if not addr[0].address in node.public_ips:
|
|
node.public_ips.append(addr[0].address)
|
|
rules.append(rule)
|
|
node.extra['port_forwarding_rules'] = rules
|
|
|
|
nodes.append(node)
|
|
|
|
return nodes
|
|
|
|
def ex_get_node(self, node_id, project=None):
|
|
"""
|
|
Return a Node object based on its ID.
|
|
|
|
:param node_id: The id of the node
|
|
:type node_id: ``str``
|
|
|
|
:keyword project: Limit node returned to those configured under
|
|
the defined project.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:rtype: :class:`CloudStackNode`
|
|
"""
|
|
list_nodes_args = {'id': node_id}
|
|
list_ips_args = {}
|
|
if project:
|
|
list_nodes_args['projectid'] = project.id
|
|
list_ips_args['projectid'] = project.id
|
|
vms = self._sync_request('listVirtualMachines', params=list_nodes_args)
|
|
if not vms:
|
|
raise Exception("Node '%s' not found" % node_id)
|
|
vm = vms['virtualmachine'][0]
|
|
addrs = self._sync_request('listPublicIpAddresses',
|
|
params=list_ips_args)
|
|
|
|
public_ips = {}
|
|
for addr in addrs.get('publicipaddress', []):
|
|
if 'virtualmachineid' not in addr:
|
|
continue
|
|
public_ips[addr['ipaddress']] = addr['id']
|
|
|
|
node = self._to_node(data=vm, public_ips=list(public_ips.keys()))
|
|
|
|
addresses = [CloudStackAddress(id=address_id, address=address,
|
|
driver=node.driver) for
|
|
address, address_id in public_ips.items()]
|
|
node.extra['ip_addresses'] = addresses
|
|
|
|
rules = []
|
|
list_fw_rules = {'virtualmachineid': node_id}
|
|
for addr in addresses:
|
|
result = self._sync_request('listIpForwardingRules',
|
|
params=list_fw_rules)
|
|
for r in result.get('ipforwardingrule', []):
|
|
if str(r['virtualmachineid']) == node.id:
|
|
rule = CloudStackIPForwardingRule(node, r['id'],
|
|
addr,
|
|
r['protocol']
|
|
.upper(),
|
|
r['startport'],
|
|
r['endport'])
|
|
rules.append(rule)
|
|
node.extra['ip_forwarding_rules'] = rules
|
|
|
|
rules = []
|
|
public_ips = self.ex_list_public_ips()
|
|
result = self._sync_request('listPortForwardingRules',
|
|
params=list_fw_rules)
|
|
for r in result.get('portforwardingrule', []):
|
|
if str(r['virtualmachineid']) == node.id:
|
|
addr = [a for a in public_ips if
|
|
a.address == r['ipaddress']]
|
|
rule = CloudStackPortForwardingRule(node, r['id'],
|
|
addr[0],
|
|
r['protocol'].upper(),
|
|
r['publicport'],
|
|
r['privateport'],
|
|
r['publicendport'],
|
|
r['privateendport'])
|
|
if not addr[0].address in node.public_ips:
|
|
node.public_ips.append(addr[0].address)
|
|
rules.append(rule)
|
|
node.extra['port_forwarding_rules'] = rules
|
|
return node
|
|
|
|
def list_sizes(self, location=None):
|
|
"""
|
|
:rtype ``list`` of :class:`NodeSize`
|
|
"""
|
|
szs = self._sync_request(command='listServiceOfferings',
|
|
method='GET')
|
|
sizes = []
|
|
for sz in szs['serviceoffering']:
|
|
extra = {'cpu': sz['cpunumber']}
|
|
sizes.append(NodeSize(sz['id'], sz['name'], sz['memory'], 0, 0,
|
|
0, self, extra=extra))
|
|
return sizes
|
|
|
|
def create_node(self, name, size, image, location=None, networks=None,
|
|
project=None, diskoffering=None, ex_keyname=None,
|
|
ex_userdata=None,
|
|
ex_security_groups=None, ex_displayname=None,
|
|
ex_ip_address=None, ex_start_vm=False,
|
|
ex_rootdisksize=None, ex_affinity_groups=None):
|
|
"""
|
|
Create a new node
|
|
|
|
@inherits: :class:`NodeDriver.create_node`
|
|
|
|
:keyword networks: Optional list of networks to launch the server
|
|
into.
|
|
:type networks: ``list`` of :class:`.CloudStackNetwork`
|
|
|
|
:keyword project: Optional project to create the new node under.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:keyword diskoffering: Optional disk offering to add to the new
|
|
node.
|
|
:type diskoffering: :class:`.CloudStackDiskOffering`
|
|
|
|
:keyword ex_keyname: Name of existing keypair
|
|
:type ex_keyname: ``str``
|
|
|
|
:keyword ex_userdata: String containing user data
|
|
:type ex_userdata: ``str``
|
|
|
|
:keyword ex_security_groups: List of security groups to assign to
|
|
the node
|
|
:type ex_security_groups: ``list`` of ``str``
|
|
|
|
:keyword ex_displayname: String containing instance display name
|
|
:type ex_displayname: ``str``
|
|
|
|
:keyword ex_ip_address: String with ipaddress for the default nic
|
|
:type ex_ip_address: ``str``
|
|
|
|
:keyword ex_start_vm: Boolean to specify to start VM after creation
|
|
Default Cloudstack behaviour is to start a VM,
|
|
if not specified.
|
|
|
|
:type ex_start_vm: ``bool``
|
|
|
|
:keyword ex_rootdisksize: String with rootdisksize for the template
|
|
:type ex_rootdisksize: ``str``
|
|
|
|
:keyword ex_affinity_groups: List of affinity groups to assign to
|
|
the node
|
|
:type ex_affinity_groups: ``list`` of
|
|
:class:`.CloudStackAffinityGroup`
|
|
|
|
:rtype: :class:`.CloudStackNode`
|
|
"""
|
|
|
|
server_params = self._create_args_to_params(
|
|
node=None,
|
|
name=name, size=size, image=image, location=location,
|
|
networks=networks, diskoffering=diskoffering,
|
|
ex_keyname=ex_keyname, ex_userdata=ex_userdata,
|
|
ex_security_groups=ex_security_groups,
|
|
ex_displayname=ex_displayname, ex_ip_address=ex_ip_address,
|
|
ex_start_vm=ex_start_vm, ex_rootdisksize=ex_rootdisksize,
|
|
ex_affinity_groups=ex_affinity_groups)
|
|
|
|
data = self._async_request(command='deployVirtualMachine',
|
|
params=server_params,
|
|
method='GET')['virtualmachine']
|
|
node = self._to_node(data=data)
|
|
return node
|
|
|
|
def _create_args_to_params(self, node, name, size, image, location=None,
|
|
networks=None,
|
|
project=None, diskoffering=None,
|
|
ex_keyname=None, ex_userdata=None,
|
|
ex_security_groups=None, ex_displayname=None,
|
|
ex_ip_address=None, ex_start_vm=False,
|
|
ex_rootdisksize=None, ex_affinity_groups=None):
|
|
server_params = {}
|
|
|
|
if name:
|
|
server_params['name'] = name
|
|
|
|
if ex_displayname:
|
|
server_params['displayname'] = ex_displayname
|
|
|
|
if size:
|
|
server_params['serviceofferingid'] = size.id
|
|
|
|
if image:
|
|
server_params['templateid'] = image.id
|
|
|
|
if location:
|
|
server_params['zoneid'] = location.id
|
|
else:
|
|
# Use a default location
|
|
server_params['zoneid'] = self.list_locations()[0].id
|
|
|
|
if networks:
|
|
networks = ','.join([str(network.id) for network in networks])
|
|
server_params['networkids'] = networks
|
|
|
|
if project:
|
|
server_params['projectid'] = project.id
|
|
|
|
if diskoffering:
|
|
server_params['diskofferingid'] = diskoffering.id
|
|
|
|
if ex_keyname:
|
|
server_params['keypair'] = ex_keyname
|
|
|
|
if ex_userdata:
|
|
ex_userdata = base64.b64encode(b(ex_userdata)).decode('ascii')
|
|
server_params['userdata'] = ex_userdata
|
|
|
|
if ex_security_groups:
|
|
ex_security_groups = ','.join(ex_security_groups)
|
|
server_params['securitygroupnames'] = ex_security_groups
|
|
|
|
if ex_ip_address:
|
|
server_params['ipaddress'] = ex_ip_address
|
|
|
|
if ex_rootdisksize:
|
|
server_params['rootdisksize'] = ex_rootdisksize
|
|
|
|
if ex_start_vm is not None:
|
|
server_params['startvm'] = ex_start_vm
|
|
|
|
if ex_affinity_groups:
|
|
affinity_group_ids = ','.join(ag.id for ag in ex_affinity_groups)
|
|
server_params['affinitygroupids'] = affinity_group_ids
|
|
|
|
return server_params
|
|
|
|
def destroy_node(self, node, ex_expunge=False):
|
|
"""
|
|
@inherits: :class:`NodeDriver.reboot_node`
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:keyword ex_expunge: If true is passed, the vm is expunged
|
|
immediately. False by default.
|
|
:type ex_expunge: ``bool``
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
args = {
|
|
'id': node.id,
|
|
}
|
|
|
|
if ex_expunge:
|
|
args['expunge'] = ex_expunge
|
|
|
|
self._async_request(command='destroyVirtualMachine',
|
|
params=args,
|
|
method='GET')
|
|
return True
|
|
|
|
def reboot_node(self, node):
|
|
"""
|
|
@inherits: :class:`NodeDriver.reboot_node`
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
self._async_request(command='rebootVirtualMachine',
|
|
params={'id': node.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_restore(self, node, template=None):
|
|
"""
|
|
Restore virtual machine
|
|
|
|
:param node: Node to restore
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param template: Optional new template
|
|
:type template: :class:`NodeImage`
|
|
|
|
:rtype ``str``
|
|
"""
|
|
params = {'virtualmachineid': node.id}
|
|
if template:
|
|
params['templateid'] = template.id
|
|
|
|
res = self._async_request(command='restoreVirtualMachine',
|
|
params=params,
|
|
method='GET')
|
|
return res['virtualmachine']['templateid']
|
|
|
|
def ex_change_node_size(self, node, offering):
|
|
"""
|
|
Change offering/size of a virtual machine
|
|
|
|
:param node: Node to change size
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param offering: The new offering
|
|
:type offering: :class:`NodeSize`
|
|
|
|
:rtype ``str``
|
|
"""
|
|
res = self._async_request(command='scaleVirtualMachine',
|
|
params={
|
|
'id': node.id,
|
|
'serviceofferingid': offering.id
|
|
},
|
|
method='GET')
|
|
return res['virtualmachine']['serviceofferingid']
|
|
|
|
def ex_start(self, node):
|
|
"""
|
|
Starts/Resumes a stopped virtual machine
|
|
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param id: The ID of the virtual machine (required)
|
|
:type id: ``str``
|
|
|
|
:param hostid: destination Host ID to deploy the VM to
|
|
parameter available for root admin only
|
|
:type hostid: ``str``
|
|
|
|
:rtype ``str``
|
|
"""
|
|
res = self._async_request(command='startVirtualMachine',
|
|
params={'id': node.id},
|
|
method='GET')
|
|
return res['virtualmachine']['state']
|
|
|
|
def ex_stop(self, node):
|
|
"""
|
|
Stops/Suspends a running virtual machine
|
|
|
|
:param node: Node to stop.
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:rtype: ``str``
|
|
"""
|
|
res = self._async_request(command='stopVirtualMachine',
|
|
params={'id': node.id},
|
|
method='GET')
|
|
return res['virtualmachine']['state']
|
|
|
|
def ex_list_disk_offerings(self):
|
|
"""
|
|
Fetch a list of all available disk offerings.
|
|
|
|
:rtype: ``list`` of :class:`CloudStackDiskOffering`
|
|
"""
|
|
|
|
diskOfferings = []
|
|
|
|
diskOfferResponse = self._sync_request(command='listDiskOfferings',
|
|
method='GET')
|
|
for diskOfferDict in diskOfferResponse.get('diskoffering', ()):
|
|
diskOfferings.append(
|
|
CloudStackDiskOffering(
|
|
id=diskOfferDict['id'],
|
|
name=diskOfferDict['name'],
|
|
size=diskOfferDict['disksize'],
|
|
customizable=diskOfferDict['iscustomized']))
|
|
|
|
return diskOfferings
|
|
|
|
def ex_list_networks(self, project=None):
|
|
"""
|
|
List the available networks
|
|
|
|
:param project: Optional project the networks belongs to.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:rtype ``list`` of :class:`CloudStackNetwork`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if project is not None:
|
|
args['projectid'] = project.id
|
|
|
|
res = self._sync_request(command='listNetworks',
|
|
params=args,
|
|
method='GET')
|
|
nets = res.get('network', [])
|
|
|
|
networks = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network']
|
|
for net in nets:
|
|
extra = self._get_extra_dict(net, extra_map)
|
|
|
|
if 'tags' in net:
|
|
extra['tags'] = self._get_resource_tags(net['tags'])
|
|
|
|
networks.append(CloudStackNetwork(
|
|
net['displaytext'],
|
|
net['name'],
|
|
net['networkofferingid'],
|
|
net['id'],
|
|
net['zoneid'],
|
|
self,
|
|
extra=extra))
|
|
|
|
return networks
|
|
|
|
def ex_list_network_offerings(self):
|
|
"""
|
|
List the available network offerings
|
|
|
|
:rtype ``list`` of :class:`CloudStackNetworkOffering`
|
|
"""
|
|
res = self._sync_request(command='listNetworkOfferings',
|
|
method='GET')
|
|
netoffers = res.get('networkoffering', [])
|
|
|
|
networkofferings = []
|
|
|
|
for netoffer in netoffers:
|
|
networkofferings.append(CloudStackNetworkOffering(
|
|
netoffer['name'],
|
|
netoffer['displaytext'],
|
|
netoffer['guestiptype'],
|
|
netoffer['id'],
|
|
netoffer['serviceofferingid'],
|
|
netoffer['forvpc'],
|
|
self))
|
|
|
|
return networkofferings
|
|
|
|
def ex_create_network(self, display_text, name, network_offering,
|
|
location, gateway=None, netmask=None,
|
|
network_domain=None, vpc_id=None, project_id=None):
|
|
"""
|
|
|
|
Creates a Network, only available in advanced zones.
|
|
|
|
:param display_text: the display text of the network
|
|
:type display_text: ``str``
|
|
|
|
:param name: the name of the network
|
|
:type name: ``str``
|
|
|
|
:param network_offering: NetworkOffering object
|
|
:type network_offering: :class:'CloudStackNetworkOffering`
|
|
|
|
:param location: Zone object
|
|
:type location: :class:`NodeLocation`
|
|
|
|
:param gateway: Optional, the Gateway of this network
|
|
:type gateway: ``str``
|
|
|
|
:param netmask: Optional, the netmask of this network
|
|
:type netmask: ``str``
|
|
|
|
:param network_domain: Optional, the DNS domain of the network
|
|
:type network_domain: ``str``
|
|
|
|
:param vpc_id: Optional, the VPC id the network belongs to
|
|
:type vpc_id: ``str``
|
|
|
|
:param project_id: Optional, the project id the networks belongs to
|
|
:type project_id: ``str``
|
|
|
|
:rtype: :class:`CloudStackNetwork`
|
|
|
|
"""
|
|
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network']
|
|
|
|
args = {
|
|
'displaytext': display_text,
|
|
'name': name,
|
|
'networkofferingid': network_offering.id,
|
|
'zoneid': location.id,
|
|
}
|
|
|
|
if gateway is not None:
|
|
args['gateway'] = gateway
|
|
|
|
if netmask is not None:
|
|
args['netmask'] = netmask
|
|
|
|
if network_domain is not None:
|
|
args['networkdomain'] = network_domain
|
|
|
|
if vpc_id is not None:
|
|
args['vpcid'] = vpc_id
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
""" Cloudstack allows for duplicate network names,
|
|
this should be handled in the code leveraging libcloud
|
|
As there could be use cases for duplicate names.
|
|
e.g. management from ROOT level"""
|
|
|
|
# for net in self.ex_list_networks():
|
|
# if name == net.name:
|
|
# raise LibcloudError('This network name already exists')
|
|
|
|
result = self._sync_request(command='createNetwork',
|
|
params=args,
|
|
method='GET')
|
|
|
|
result = result['network']
|
|
extra = self._get_extra_dict(result, extra_map)
|
|
|
|
network = CloudStackNetwork(display_text,
|
|
name,
|
|
network_offering.id,
|
|
result['id'],
|
|
location.id,
|
|
self,
|
|
extra=extra)
|
|
|
|
return network
|
|
|
|
def ex_delete_network(self, network, force=None):
|
|
"""
|
|
|
|
Deletes a Network, only available in advanced zones.
|
|
|
|
:param network: The network
|
|
:type network: :class: 'CloudStackNetwork'
|
|
|
|
:param force: Force deletion of the network?
|
|
:type force: ``bool``
|
|
|
|
:rtype: ``bool``
|
|
|
|
"""
|
|
|
|
args = {'id': network.id, 'forced': force}
|
|
|
|
self._async_request(command='deleteNetwork',
|
|
params=args,
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_list_vpc_offerings(self):
|
|
"""
|
|
List the available vpc offerings
|
|
|
|
:rtype ``list`` of :class:`CloudStackVPCOffering`
|
|
"""
|
|
res = self._sync_request(command='listVPCOfferings',
|
|
method='GET')
|
|
vpcoffers = res.get('vpcoffering', [])
|
|
|
|
vpcofferings = []
|
|
|
|
for vpcoffer in vpcoffers:
|
|
vpcofferings.append(CloudStackVPCOffering(
|
|
vpcoffer['name'],
|
|
vpcoffer['displaytext'],
|
|
vpcoffer['id'],
|
|
self))
|
|
|
|
return vpcofferings
|
|
|
|
def ex_list_vpcs(self, project=None):
|
|
"""
|
|
List the available VPCs
|
|
|
|
:keyword project: Optional project under which VPCs are present.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:rtype ``list`` of :class:`CloudStackVPC`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if project is not None:
|
|
args['projectid'] = project.id
|
|
|
|
res = self._sync_request(command='listVPCs',
|
|
params=args,
|
|
method='GET')
|
|
vpcs = res.get('vpc', [])
|
|
|
|
networks = []
|
|
for vpc in vpcs:
|
|
|
|
networks.append(CloudStackVPC(
|
|
vpc['name'],
|
|
vpc['vpcofferingid'],
|
|
vpc['id'],
|
|
vpc['cidr'],
|
|
self,
|
|
vpc['zoneid'],
|
|
vpc['displaytext']))
|
|
|
|
return networks
|
|
|
|
def ex_list_routers(self, vpc_id=None):
|
|
"""
|
|
List routers
|
|
|
|
:rtype ``list`` of :class:`CloudStackRouter`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if vpc_id is not None:
|
|
args['vpcid'] = vpc_id
|
|
|
|
res = self._sync_request(command='listRouters',
|
|
params=args,
|
|
method='GET')
|
|
rts = res.get('router', [])
|
|
|
|
routers = []
|
|
for router in rts:
|
|
|
|
routers.append(CloudStackRouter(
|
|
router['id'],
|
|
router['name'],
|
|
router['state'],
|
|
router['publicip'],
|
|
router['vpcid'],
|
|
self))
|
|
|
|
return routers
|
|
|
|
def ex_create_vpc(self, cidr, display_text, name, vpc_offering,
|
|
zone_id, network_domain=None):
|
|
"""
|
|
|
|
Creates a VPC, only available in advanced zones.
|
|
|
|
:param cidr: the cidr of the VPC. All VPC guest networks' cidrs
|
|
should be within this CIDR
|
|
|
|
:type display_text: ``str``
|
|
|
|
:param display_text: the display text of the VPC
|
|
:type display_text: ``str``
|
|
|
|
:param name: the name of the VPC
|
|
:type name: ``str``
|
|
|
|
:param vpc_offering: the ID of the VPC offering
|
|
:type vpc_offering: :class:'CloudStackVPCOffering`
|
|
|
|
:param zone_id: the ID of the availability zone
|
|
:type zone_id: ``str``
|
|
|
|
:param network_domain: Optional, the DNS domain of the network
|
|
:type network_domain: ``str``
|
|
|
|
:rtype: :class:`CloudStackVPC`
|
|
|
|
"""
|
|
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpc']
|
|
|
|
args = {
|
|
'cidr': cidr,
|
|
'displaytext': display_text,
|
|
'name': name,
|
|
'vpcofferingid': vpc_offering.id,
|
|
'zoneid': zone_id,
|
|
}
|
|
|
|
if network_domain is not None:
|
|
args['networkdomain'] = network_domain
|
|
|
|
result = self._sync_request(command='createVPC',
|
|
params=args,
|
|
method='GET')
|
|
|
|
extra = self._get_extra_dict(result, extra_map)
|
|
|
|
vpc = CloudStackVPC(name,
|
|
vpc_offering.id,
|
|
result['id'],
|
|
cidr,
|
|
self,
|
|
zone_id,
|
|
display_text,
|
|
extra=extra)
|
|
|
|
return vpc
|
|
|
|
def ex_delete_vpc(self, vpc):
|
|
"""
|
|
|
|
Deletes a VPC, only available in advanced zones.
|
|
|
|
:param vpc: The VPC
|
|
:type vpc: :class: 'CloudStackVPC'
|
|
|
|
:rtype: ``bool``
|
|
|
|
"""
|
|
|
|
args = {'id': vpc.id}
|
|
|
|
self._async_request(command='deleteVPC',
|
|
params=args,
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_list_projects(self):
|
|
"""
|
|
List the available projects
|
|
|
|
:rtype ``list`` of :class:`CloudStackProject`
|
|
"""
|
|
|
|
res = self._sync_request(command='listProjects',
|
|
method='GET')
|
|
projs = res.get('project', [])
|
|
|
|
projects = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['project']
|
|
for proj in projs:
|
|
extra = self._get_extra_dict(proj, extra_map)
|
|
|
|
if 'tags' in proj:
|
|
extra['tags'] = self._get_resource_tags(proj['tags'])
|
|
|
|
projects.append(CloudStackProject(
|
|
id=proj['id'],
|
|
name=proj['name'],
|
|
display_text=proj['displaytext'],
|
|
driver=self,
|
|
extra=extra))
|
|
|
|
return projects
|
|
|
|
def create_volume(self, size, name, location=None, snapshot=None,
|
|
ex_volume_type=None):
|
|
"""
|
|
Creates a data volume
|
|
Defaults to the first location
|
|
"""
|
|
if ex_volume_type is None:
|
|
for diskOffering in self.ex_list_disk_offerings():
|
|
if diskOffering.size == size or diskOffering.customizable:
|
|
break
|
|
else:
|
|
raise LibcloudError(
|
|
'Disk offering with size=%s not found' % size)
|
|
else:
|
|
for diskOffering in self.ex_list_disk_offerings():
|
|
if diskOffering.name == ex_volume_type:
|
|
if not diskOffering.customizable:
|
|
size = diskOffering.size
|
|
break
|
|
else:
|
|
raise LibcloudError(
|
|
'Volume type with name=%s not found' % ex_volume_type)
|
|
|
|
if location is None:
|
|
location = self.list_locations()[0]
|
|
|
|
params = {'name': name,
|
|
'diskOfferingId': diskOffering.id,
|
|
'zoneId': location.id}
|
|
|
|
if diskOffering.customizable:
|
|
params['size'] = size
|
|
|
|
requestResult = self._async_request(command='createVolume',
|
|
params=params,
|
|
method='GET')
|
|
|
|
volumeResponse = requestResult['volume']
|
|
|
|
state = self._to_volume_state(volumeResponse)
|
|
|
|
return StorageVolume(id=volumeResponse['id'],
|
|
name=name,
|
|
size=size,
|
|
state=state,
|
|
driver=self,
|
|
extra=dict(name=volumeResponse['name']))
|
|
|
|
def destroy_volume(self, volume):
|
|
"""
|
|
:rtype: ``bool``
|
|
"""
|
|
self._sync_request(command='deleteVolume',
|
|
params={'id': volume.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def attach_volume(self, node, volume, device=None):
|
|
"""
|
|
@inherits: :class:`NodeDriver.attach_volume`
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
# TODO Add handling for device name
|
|
self._async_request(command='attachVolume',
|
|
params={'id': volume.id,
|
|
'virtualMachineId': node.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def detach_volume(self, volume):
|
|
"""
|
|
:rtype: ``bool``
|
|
"""
|
|
self._async_request(command='detachVolume',
|
|
params={'id': volume.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def list_volumes(self, node=None):
|
|
"""
|
|
List all volumes
|
|
|
|
:param node: Only return volumes for the provided node.
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:rtype: ``list`` of :class:`StorageVolume`
|
|
"""
|
|
if node:
|
|
volumes = self._sync_request(command='listVolumes',
|
|
params={'virtualmachineid': node.id},
|
|
method='GET')
|
|
else:
|
|
volumes = self._sync_request(command='listVolumes',
|
|
method='GET')
|
|
|
|
list_volumes = []
|
|
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['volume']
|
|
for vol in volumes.get('volume', []):
|
|
extra = self._get_extra_dict(vol, extra_map)
|
|
|
|
if 'tags' in vol:
|
|
extra['tags'] = self._get_resource_tags(vol['tags'])
|
|
|
|
state = self._to_volume_state(vol)
|
|
|
|
list_volumes.append(StorageVolume(id=vol['id'],
|
|
name=vol['name'],
|
|
size=vol['size'],
|
|
state=state,
|
|
driver=self,
|
|
extra=extra))
|
|
return list_volumes
|
|
|
|
def ex_get_volume(self, volume_id, project=None):
|
|
"""
|
|
Return a StorageVolume object based on its ID.
|
|
|
|
:param volume_id: The id of the volume
|
|
:type volume_id: ``str``
|
|
|
|
:keyword project: Limit volume returned to those configured under
|
|
the defined project.
|
|
:type project: :class:`.CloudStackProject`
|
|
|
|
:rtype: :class:`CloudStackNode`
|
|
"""
|
|
args = {'id': volume_id}
|
|
if project:
|
|
args['projectid'] = project.id
|
|
volumes = self._sync_request(command='listVolumes', params=args)
|
|
if not volumes:
|
|
raise Exception("Volume '%s' not found" % volume_id)
|
|
vol = volumes['volume'][0]
|
|
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['volume']
|
|
extra = self._get_extra_dict(vol, extra_map)
|
|
|
|
if 'tags' in vol:
|
|
extra['tags'] = self._get_resource_tags(vol['tags'])
|
|
|
|
state = self._to_volume_state(vol)
|
|
|
|
volume = StorageVolume(id=vol['id'], name=vol['name'], state=state,
|
|
size=vol['size'], driver=self, extra=extra)
|
|
return volume
|
|
|
|
def list_key_pairs(self, **kwargs):
|
|
"""
|
|
List registered key pairs.
|
|
|
|
:param projectid: list objects by project
|
|
:type projectid: ``str``
|
|
|
|
:param page: The page to list the keypairs from
|
|
:type page: ``int``
|
|
|
|
:param keyword: List by keyword
|
|
:type keyword: ``str``
|
|
|
|
:param listall: If set to false, list only resources
|
|
belonging to the command's caller;
|
|
if set to true - list resources that
|
|
the caller is authorized to see.
|
|
Default value is false
|
|
|
|
:type listall: ``bool``
|
|
|
|
:param pagesize: The number of results per page
|
|
:type pagesize: ``int``
|
|
|
|
:param account: List resources by account.
|
|
Must be used with the domainId parameter
|
|
:type account: ``str``
|
|
|
|
:param isrecursive: Defaults to false, but if true,
|
|
lists all resources from
|
|
the parent specified by the
|
|
domainId till leaves.
|
|
:type isrecursive: ``bool``
|
|
|
|
:param fingerprint: A public key fingerprint to look for
|
|
:type fingerprint: ``str``
|
|
|
|
:param name: A key pair name to look for
|
|
:type name: ``str``
|
|
|
|
:param domainid: List only resources belonging to
|
|
the domain specified
|
|
:type domainid: ``str``
|
|
|
|
:return: A list of key par objects.
|
|
:rtype: ``list`` of :class:`libcloud.compute.base.KeyPair`
|
|
"""
|
|
extra_args = kwargs.copy()
|
|
res = self._sync_request(command='listSSHKeyPairs',
|
|
params=extra_args,
|
|
method='GET')
|
|
key_pairs = res.get('sshkeypair', [])
|
|
key_pairs = self._to_key_pairs(data=key_pairs)
|
|
return key_pairs
|
|
|
|
def get_key_pair(self, name):
|
|
"""
|
|
Retrieve a single key pair.
|
|
|
|
:param name: Name of the key pair to retrieve.
|
|
:type name: ``str``
|
|
|
|
:rtype: :class:`.KeyPair`
|
|
"""
|
|
params = {'name': name}
|
|
res = self._sync_request(command='listSSHKeyPairs',
|
|
params=params,
|
|
method='GET')
|
|
key_pairs = res.get('sshkeypair', [])
|
|
|
|
if len(key_pairs) == 0:
|
|
raise KeyPairDoesNotExistError(name=name, driver=self)
|
|
|
|
key_pair = self._to_key_pair(data=key_pairs[0])
|
|
return key_pair
|
|
|
|
def create_key_pair(self, name, **kwargs):
|
|
"""
|
|
Create a new key pair object.
|
|
|
|
:param name: Key pair name.
|
|
:type name: ``str``
|
|
|
|
:param name: Name of the keypair (required)
|
|
:type name: ``str``
|
|
|
|
:param projectid: An optional project for the ssh key
|
|
:type projectid: ``str``
|
|
|
|
:param domainid: An optional domainId for the ssh key.
|
|
If the account parameter is used,
|
|
domainId must also be used.
|
|
:type domainid: ``str``
|
|
|
|
:param account: An optional account for the ssh key.
|
|
Must be used with domainId.
|
|
:type account: ``str``
|
|
|
|
:return: Created key pair object.
|
|
:rtype: :class:`libcloud.compute.base.KeyPair`
|
|
"""
|
|
extra_args = kwargs.copy()
|
|
|
|
params = {'name': name}
|
|
params.update(extra_args)
|
|
|
|
res = self._sync_request(command='createSSHKeyPair',
|
|
params=params,
|
|
method='GET')
|
|
key_pair = self._to_key_pair(data=res['keypair'])
|
|
return key_pair
|
|
|
|
def import_key_pair_from_string(self, name, key_material):
|
|
"""
|
|
Import a new public key from string.
|
|
|
|
:param name: Key pair name.
|
|
:type name: ``str``
|
|
|
|
:param key_material: Public key material.
|
|
:type key_material: ``str``
|
|
|
|
:return: Imported key pair object.
|
|
:rtype: :class:`libcloud.compute.base.KeyPair`
|
|
"""
|
|
res = self._sync_request(command='registerSSHKeyPair',
|
|
params={'name': name,
|
|
'publickey': key_material},
|
|
method='GET')
|
|
key_pair = self._to_key_pair(data=res['keypair'])
|
|
return key_pair
|
|
|
|
def delete_key_pair(self, key_pair, **kwargs):
|
|
"""
|
|
Delete an existing key pair.
|
|
|
|
:param key_pair: Key pair object.
|
|
:type key_pair: :class:`libcloud.compute.base.KeyPair`
|
|
|
|
:param projectid: The project associated with keypair
|
|
:type projectid: ``str``
|
|
|
|
:param domainid: The domain ID associated with the keypair
|
|
:type domainid: ``str``
|
|
|
|
:param account: The account associated with the keypair.
|
|
Must be used with the domainId parameter.
|
|
:type account: ``str``
|
|
|
|
:return: True of False based on success of Keypair deletion
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
extra_args = kwargs.copy()
|
|
params = {'name': key_pair.name}
|
|
params.update(extra_args)
|
|
|
|
res = self._sync_request(command='deleteSSHKeyPair',
|
|
params=params,
|
|
method='GET')
|
|
return res['success'] == 'true'
|
|
|
|
def ex_list_public_ips(self):
|
|
"""
|
|
Lists all Public IP Addresses.
|
|
|
|
:rtype: ``list`` of :class:`CloudStackAddress`
|
|
"""
|
|
ips = []
|
|
|
|
res = self._sync_request(command='listPublicIpAddresses',
|
|
method='GET')
|
|
|
|
# Workaround for basic zones
|
|
if not res:
|
|
return ips
|
|
|
|
for ip in res['publicipaddress']:
|
|
ips.append(CloudStackAddress(ip['id'],
|
|
ip['ipaddress'],
|
|
self,
|
|
ip.get('associatednetworkid', []),
|
|
ip.get('vpcid'),
|
|
ip.get('virtualmachineid')))
|
|
|
|
return ips
|
|
|
|
def ex_allocate_public_ip(self, vpc_id=None, network_id=None,
|
|
location=None):
|
|
"""
|
|
Allocate a public IP.
|
|
|
|
:param vpc_id: VPC the ip belongs to
|
|
:type vpc_id: ``str``
|
|
|
|
:param network_id: Network where this IP is connected to.
|
|
:type network_id: ''str''
|
|
|
|
:param location: Zone
|
|
:type location: :class:`NodeLocation`
|
|
|
|
:rtype: :class:`CloudStackAddress`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if location is not None:
|
|
args['zoneid'] = location.id
|
|
else:
|
|
args['zoneid'] = self.list_locations()[0].id
|
|
|
|
if vpc_id is not None:
|
|
args['vpcid'] = vpc_id
|
|
|
|
if network_id is not None:
|
|
args['networkid'] = network_id
|
|
|
|
addr = self._async_request(command='associateIpAddress',
|
|
params=args,
|
|
method='GET')
|
|
addr = addr['ipaddress']
|
|
addr = CloudStackAddress(addr['id'], addr['ipaddress'], self)
|
|
return addr
|
|
|
|
def ex_release_public_ip(self, address):
|
|
"""
|
|
Release a public IP.
|
|
|
|
:param address: CloudStackAddress which should be used
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='disassociateIpAddress',
|
|
params={'id': address.id},
|
|
method='GET')
|
|
return res['success']
|
|
|
|
def ex_list_firewall_rules(self):
|
|
"""
|
|
Lists all Firewall Rules
|
|
|
|
:rtype: ``list`` of :class:`CloudStackFirewallRule`
|
|
"""
|
|
rules = []
|
|
result = self._sync_request(command='listFirewallRules',
|
|
method='GET')
|
|
if result != {}:
|
|
public_ips = self.ex_list_public_ips()
|
|
for rule in result['firewallrule']:
|
|
addr = [a for a in public_ips if
|
|
a.address == rule['ipaddress']]
|
|
|
|
rules.append(CloudStackFirewallRule(rule['id'],
|
|
addr[0],
|
|
rule['cidrlist'],
|
|
rule['protocol'],
|
|
rule.get('icmpcode'),
|
|
rule.get('icmptype'),
|
|
rule.get('startport'),
|
|
rule.get('endport')))
|
|
|
|
return rules
|
|
|
|
def ex_create_firewall_rule(self, address, cidr_list, protocol,
|
|
icmp_code=None, icmp_type=None,
|
|
start_port=None, end_port=None):
|
|
"""
|
|
Creates a Firewall Rule
|
|
|
|
:param address: External IP address
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param cidr_list: cidr list
|
|
:type cidr_list: ``str``
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param icmp_code: Error code for this icmp message
|
|
:type icmp_code: ``int``
|
|
|
|
:param icmp_type: Type of the icmp message being sent
|
|
:type icmp_type: ``int``
|
|
|
|
:param start_port: start of port range
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: end of port range
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackFirewallRule`
|
|
"""
|
|
args = {
|
|
'ipaddressid': address.id,
|
|
'cidrlist': cidr_list,
|
|
'protocol': protocol
|
|
}
|
|
if icmp_code is not None:
|
|
args['icmpcode'] = int(icmp_code)
|
|
if icmp_type is not None:
|
|
args['icmptype'] = int(icmp_type)
|
|
if start_port is not None:
|
|
args['startport'] = int(start_port)
|
|
if end_port is not None:
|
|
args['endport'] = int(end_port)
|
|
result = self._async_request(command='createFirewallRule',
|
|
params=args,
|
|
method='GET')
|
|
rule = CloudStackFirewallRule(result['firewallrule']['id'],
|
|
address,
|
|
cidr_list,
|
|
protocol,
|
|
icmp_code,
|
|
icmp_type,
|
|
start_port,
|
|
end_port)
|
|
return rule
|
|
|
|
def ex_delete_firewall_rule(self, firewall_rule):
|
|
"""
|
|
Remove a Firewall Rule.
|
|
|
|
:param firewall_rule: Firewall rule which should be used
|
|
:type firewall_rule: :class:`CloudStackFirewallRule`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='deleteFirewallRule',
|
|
params={'id': firewall_rule.id},
|
|
method='GET')
|
|
return res['success']
|
|
|
|
def ex_list_egress_firewall_rules(self):
|
|
"""
|
|
Lists all egress Firewall Rules
|
|
|
|
:rtype: ``list`` of :class:`CloudStackEgressFirewallRule`
|
|
"""
|
|
rules = []
|
|
result = self._sync_request(command='listEgressFirewallRules',
|
|
method='GET')
|
|
for rule in result['firewallrule']:
|
|
rules.append(CloudStackEgressFirewallRule(rule['id'],
|
|
rule['networkid'],
|
|
rule['cidrlist'],
|
|
rule['protocol'],
|
|
rule.get('icmpcode'),
|
|
rule.get('icmptype'),
|
|
rule.get('startport'),
|
|
rule.get('endport')))
|
|
|
|
return rules
|
|
|
|
def ex_create_egress_firewall_rule(self, network_id, cidr_list, protocol,
|
|
icmp_code=None, icmp_type=None,
|
|
start_port=None, end_port=None):
|
|
"""
|
|
Creates a Firewall Rule
|
|
|
|
:param network_id: the id network network for the egress firewall
|
|
services
|
|
:type network_id: ``str``
|
|
|
|
:param cidr_list: cidr list
|
|
:type cidr_list: ``str``
|
|
|
|
:param protocol: TCP/IP Protocol (TCP, UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param icmp_code: Error code for this icmp message
|
|
:type icmp_code: ``int``
|
|
|
|
:param icmp_type: Type of the icmp message being sent
|
|
:type icmp_type: ``int``
|
|
|
|
:param start_port: start of port range
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: end of port range
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackEgressFirewallRule`
|
|
"""
|
|
args = {
|
|
'networkid': network_id,
|
|
'cidrlist': cidr_list,
|
|
'protocol': protocol
|
|
}
|
|
if icmp_code is not None:
|
|
args['icmpcode'] = int(icmp_code)
|
|
if icmp_type is not None:
|
|
args['icmptype'] = int(icmp_type)
|
|
if start_port is not None:
|
|
args['startport'] = int(start_port)
|
|
if end_port is not None:
|
|
args['endport'] = int(end_port)
|
|
|
|
result = self._async_request(command='createEgressFirewallRule',
|
|
params=args,
|
|
method='GET')
|
|
|
|
rule = CloudStackEgressFirewallRule(result['firewallrule']['id'],
|
|
network_id,
|
|
cidr_list,
|
|
protocol,
|
|
icmp_code,
|
|
icmp_type,
|
|
start_port,
|
|
end_port)
|
|
return rule
|
|
|
|
def ex_delete_egress_firewall_rule(self, firewall_rule):
|
|
"""
|
|
Remove a Firewall rule.
|
|
|
|
:param egress_firewall_rule: Firewall rule which should be used
|
|
:type egress_firewall_rule: :class:`CloudStackEgressFirewallRule`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='deleteEgressFirewallRule',
|
|
params={'id': firewall_rule.id},
|
|
method='GET')
|
|
return res['success']
|
|
|
|
def ex_list_port_forwarding_rules(self, account=None, domain_id=None,
|
|
id=None, ipaddress_id=None,
|
|
is_recursive=None, keyword=None,
|
|
list_all=None, network_id=None,
|
|
page=None, page_size=None,
|
|
project_id=None):
|
|
"""
|
|
Lists all Port Forwarding Rules
|
|
|
|
:param account: List resources by account.
|
|
Must be used with the domainId parameter
|
|
:type account: ``str``
|
|
|
|
:param domain_id: List only resources belonging to
|
|
the domain specified
|
|
:type domain_id: ``str``
|
|
|
|
:param for_display: List resources by display flag (only root
|
|
admin is eligible to pass this parameter).
|
|
:type for_display: ``bool``
|
|
|
|
:param id: Lists rule with the specified ID
|
|
:type id: ``str``
|
|
|
|
:param ipaddress_id: list the rule belonging to
|
|
this public ip address
|
|
:type ipaddress_id: ``str``
|
|
|
|
:param is_recursive: Defaults to false, but if true,
|
|
lists all resources from
|
|
the parent specified by the
|
|
domainId till leaves.
|
|
:type is_recursive: ``bool``
|
|
|
|
:param keyword: List by keyword
|
|
:type keyword: ``str``
|
|
|
|
:param list_all: If set to false, list only resources
|
|
belonging to the command's caller;
|
|
if set to true - list resources that
|
|
the caller is authorized to see.
|
|
Default value is false
|
|
:type list_all: ``bool``
|
|
|
|
:param network_id: list port forwarding rules for certain network
|
|
:type network_id: ``string``
|
|
|
|
:param page: The page to list the keypairs from
|
|
:type page: ``int``
|
|
|
|
:param page_size: The number of results per page
|
|
:type page_size: ``int``
|
|
|
|
:param project_id: list objects by project
|
|
:type project_id: ``str``
|
|
|
|
:rtype: ``list`` of :class:`CloudStackPortForwardingRule`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if id is not None:
|
|
args['id'] = id
|
|
|
|
if ipaddress_id is not None:
|
|
args['ipaddressid'] = ipaddress_id
|
|
|
|
if is_recursive is not None:
|
|
args['isrecursive'] = is_recursive
|
|
|
|
if keyword is not None:
|
|
args['keyword'] = keyword
|
|
|
|
if list_all is not None:
|
|
args['listall'] = list_all
|
|
|
|
if network_id is not None:
|
|
args['networkid'] = network_id
|
|
|
|
if page is not None:
|
|
args['page'] = page
|
|
|
|
if page_size is not None:
|
|
args['pagesize'] = page_size
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
rules = []
|
|
result = self._sync_request(command='listPortForwardingRules',
|
|
params=args,
|
|
method='GET')
|
|
if result != {}:
|
|
public_ips = self.ex_list_public_ips()
|
|
nodes = self.list_nodes()
|
|
for rule in result['portforwardingrule']:
|
|
node = [n for n in nodes
|
|
if n.id == str(rule['virtualmachineid'])]
|
|
addr = [a for a in public_ips if
|
|
a.address == rule['ipaddress']]
|
|
rules.append(CloudStackPortForwardingRule
|
|
(node[0],
|
|
rule['id'],
|
|
addr[0],
|
|
rule['protocol'],
|
|
rule['publicport'],
|
|
rule['privateport'],
|
|
rule['publicendport'],
|
|
rule['privateendport']))
|
|
|
|
return rules
|
|
|
|
def ex_create_port_forwarding_rule(self, node, address,
|
|
private_port, public_port,
|
|
protocol,
|
|
public_end_port=None,
|
|
private_end_port=None,
|
|
openfirewall=True,
|
|
network_id=None):
|
|
"""
|
|
Creates a Port Forwarding Rule, used for Source NAT
|
|
|
|
:param address: IP address of the Source NAT
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param private_port: Port of the virtual machine
|
|
:type private_port: ``int``
|
|
|
|
:param protocol: Protocol of the rule
|
|
:type protocol: ``str``
|
|
|
|
:param public_port: Public port on the Source NAT address
|
|
:type public_port: ``int``
|
|
|
|
:param node: The virtual machine
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param network_id: The network of the vm the Port Forwarding rule
|
|
will be created for. Required when public Ip
|
|
address is not associated with any Guest
|
|
network yet (VPC case)
|
|
:type network_id: ``string``
|
|
|
|
:rtype: :class:`CloudStackPortForwardingRule`
|
|
"""
|
|
args = {
|
|
'ipaddressid': address.id,
|
|
'protocol': protocol,
|
|
'privateport': int(private_port),
|
|
'publicport': int(public_port),
|
|
'virtualmachineid': node.id,
|
|
'openfirewall': openfirewall
|
|
}
|
|
if public_end_port:
|
|
args['publicendport'] = int(public_end_port)
|
|
if private_end_port:
|
|
args['privateendport'] = int(private_end_port)
|
|
if network_id:
|
|
args['networkid'] = network_id
|
|
|
|
result = self._async_request(command='createPortForwardingRule',
|
|
params=args,
|
|
method='GET')
|
|
rule = CloudStackPortForwardingRule(node,
|
|
result['portforwardingrule']
|
|
['id'],
|
|
address,
|
|
protocol,
|
|
public_port,
|
|
private_port,
|
|
public_end_port,
|
|
private_end_port,
|
|
network_id)
|
|
node.extra['port_forwarding_rules'].append(rule)
|
|
node.public_ips.append(address.address)
|
|
return rule
|
|
|
|
def ex_delete_port_forwarding_rule(self, node, rule):
|
|
"""
|
|
Remove a Port forwarding rule.
|
|
|
|
:param node: Node used in the rule
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param rule: Forwarding rule which should be used
|
|
:type rule: :class:`CloudStackPortForwardingRule`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
node.extra['port_forwarding_rules'].remove(rule)
|
|
node.public_ips.remove(rule.address.address)
|
|
res = self._async_request(command='deletePortForwardingRule',
|
|
params={'id': rule.id},
|
|
method='GET')
|
|
return res['success']
|
|
|
|
def ex_list_ip_forwarding_rules(self, account=None, domain_id=None,
|
|
id=None, ipaddress_id=None,
|
|
is_recursive=None, keyword=None,
|
|
list_all=None, page=None, page_size=None,
|
|
project_id=None, virtualmachine_id=None):
|
|
"""
|
|
Lists all NAT/firewall forwarding rules
|
|
|
|
:param account: List resources by account.
|
|
Must be used with the domainId parameter
|
|
:type account: ``str``
|
|
|
|
:param domain_id: List only resources belonging to
|
|
the domain specified
|
|
:type domain_id: ``str``
|
|
|
|
:param id: Lists rule with the specified ID
|
|
:type id: ``str``
|
|
|
|
:param ipaddress_id: list the rule belonging to
|
|
this public ip address
|
|
:type ipaddress_id: ``str``
|
|
|
|
:param is_recursive: Defaults to false, but if true,
|
|
lists all resources from
|
|
the parent specified by the
|
|
domainId till leaves.
|
|
:type is_recursive: ``bool``
|
|
|
|
:param keyword: List by keyword
|
|
:type keyword: ``str``
|
|
|
|
:param list_all: If set to false, list only resources
|
|
belonging to the command's caller;
|
|
if set to true - list resources that
|
|
the caller is authorized to see.
|
|
Default value is false
|
|
:type list_all: ``bool``
|
|
|
|
:param page: The page to list the keypairs from
|
|
:type page: ``int``
|
|
|
|
:param page_size: The number of results per page
|
|
:type page_size: ``int``
|
|
|
|
:param project_id: list objects by project
|
|
:type project_id: ``str``
|
|
|
|
:param virtualmachine_id: Lists all rules applied to
|
|
the specified Vm
|
|
:type virtualmachine_id: ``str``
|
|
|
|
:rtype: ``list`` of :class:`CloudStackIPForwardingRule`
|
|
"""
|
|
|
|
args = {}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if id is not None:
|
|
args['id'] = id
|
|
|
|
if ipaddress_id is not None:
|
|
args['ipaddressid'] = ipaddress_id
|
|
|
|
if is_recursive is not None:
|
|
args['isrecursive'] = is_recursive
|
|
|
|
if keyword is not None:
|
|
args['keyword'] = keyword
|
|
|
|
if list_all is not None:
|
|
args['listall'] = list_all
|
|
|
|
if page is not None:
|
|
args['page'] = page
|
|
|
|
if page_size is not None:
|
|
args['pagesize'] = page_size
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
if virtualmachine_id is not None:
|
|
args['virtualmachineid'] = virtualmachine_id
|
|
|
|
result = self._sync_request(command='listIpForwardingRules',
|
|
params=args,
|
|
method='GET')
|
|
|
|
rules = []
|
|
if result != {}:
|
|
public_ips = self.ex_list_public_ips()
|
|
nodes = self.list_nodes()
|
|
for rule in result['ipforwardingrule']:
|
|
node = [n for n in nodes
|
|
if n.id == str(rule['virtualmachineid'])]
|
|
addr = [a for a in public_ips if
|
|
a.address == rule['ipaddress']]
|
|
rules.append(CloudStackIPForwardingRule
|
|
(node[0],
|
|
rule['id'],
|
|
addr[0],
|
|
rule['protocol'],
|
|
rule['startport'],
|
|
rule['endport']))
|
|
return rules
|
|
|
|
def ex_create_ip_forwarding_rule(self, node, address, protocol,
|
|
start_port, end_port=None):
|
|
"""
|
|
"Add a NAT/firewall forwarding rule.
|
|
|
|
:param node: Node which should be used
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param address: CloudStackAddress which should be used
|
|
:type address: :class:`CloudStackAddress`
|
|
|
|
:param protocol: Protocol which should be used (TCP or UDP)
|
|
:type protocol: ``str``
|
|
|
|
:param start_port: Start port which should be used
|
|
:type start_port: ``int``
|
|
|
|
:param end_port: End port which should be used
|
|
:type end_port: ``int``
|
|
|
|
:rtype: :class:`CloudStackForwardingRule`
|
|
"""
|
|
|
|
protocol = protocol.upper()
|
|
if protocol not in ('TCP', 'UDP'):
|
|
return None
|
|
|
|
args = {
|
|
'ipaddressid': address.id,
|
|
'protocol': protocol,
|
|
'startport': int(start_port)
|
|
}
|
|
if end_port is not None:
|
|
args['endport'] = int(end_port)
|
|
|
|
result = self._async_request(command='createIpForwardingRule',
|
|
params=args,
|
|
method='GET')
|
|
result = result['ipforwardingrule']
|
|
rule = CloudStackIPForwardingRule(node, result['id'], address,
|
|
protocol, start_port, end_port)
|
|
node.extra['ip_forwarding_rules'].append(rule)
|
|
return rule
|
|
|
|
def ex_delete_ip_forwarding_rule(self, node, rule):
|
|
"""
|
|
Remove a NAT/firewall forwarding rule.
|
|
|
|
:param node: Node which should be used
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param rule: Forwarding rule which should be used
|
|
:type rule: :class:`CloudStackForwardingRule`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
node.extra['ip_forwarding_rules'].remove(rule)
|
|
self._async_request(command='deleteIpForwardingRule',
|
|
params={'id': rule.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_create_network_acllist(self, name, vpc_id, description=None):
|
|
"""
|
|
Create an ACL List for a network within a VPC.
|
|
|
|
:param name: Name of the network ACL List
|
|
:type name: ``string``
|
|
|
|
:param vpc_id: Id of the VPC associated with this network ACL List
|
|
:type vpc_id: ``string``
|
|
|
|
:param description: Description of the network ACL List
|
|
:type description: ``string``
|
|
|
|
:rtype: :class:`CloudStackNetworkACLList`
|
|
"""
|
|
|
|
args = {
|
|
'name': name,
|
|
'vpcid': vpc_id
|
|
}
|
|
if description:
|
|
args['description'] = description
|
|
|
|
result = self._sync_request(command='createNetworkACLList',
|
|
params=args,
|
|
method='GET')
|
|
|
|
acl_list = CloudStackNetworkACLList(result['id'],
|
|
name,
|
|
vpc_id,
|
|
self,
|
|
description)
|
|
return acl_list
|
|
|
|
def ex_create_network_acl(self, protocol, acl_id, cidr_list,
|
|
start_port, end_port, action=None,
|
|
traffic_type=None):
|
|
"""
|
|
Creates an ACL rule in the given network (the network has to belong to
|
|
VPC)
|
|
|
|
:param protocol: the protocol for the ACL rule. Valid values are
|
|
TCP/UDP/ICMP/ALL or valid protocol number
|
|
:type protocol: ``string``
|
|
|
|
:param acl_id: Name of the network ACL List
|
|
:type acl_id: ``str``
|
|
|
|
:param cidr_list: the cidr list to allow traffic from/to
|
|
:type cidr_list: ``str``
|
|
|
|
:param start_port: the starting port of ACL
|
|
:type start_port: ``str``
|
|
|
|
:param end_port: the ending port of ACL
|
|
:type end_port: ``str``
|
|
|
|
:param action: scl entry action, allow or deny
|
|
:type action: ``str``
|
|
|
|
:param traffic_type: the traffic type for the ACL,can be Ingress
|
|
or Egress, defaulted to Ingress if not specified
|
|
:type traffic_type: ``str``
|
|
|
|
:rtype: :class:`CloudStackNetworkACL`
|
|
"""
|
|
|
|
args = {
|
|
'protocol': protocol,
|
|
'aclid': acl_id,
|
|
'cidrlist': cidr_list,
|
|
'startport': start_port,
|
|
'endport': end_port
|
|
}
|
|
|
|
if action:
|
|
args['action'] = action
|
|
else:
|
|
action = "allow"
|
|
|
|
if traffic_type:
|
|
args['traffictype'] = traffic_type
|
|
|
|
result = self._async_request(command='createNetworkACL',
|
|
params=args,
|
|
method='GET')
|
|
|
|
acl = CloudStackNetworkACL(result['networkacl']['id'],
|
|
protocol,
|
|
acl_id,
|
|
action,
|
|
cidr_list,
|
|
start_port,
|
|
end_port,
|
|
traffic_type)
|
|
|
|
return acl
|
|
|
|
def ex_list_network_acllists(self):
|
|
"""
|
|
Lists all network ACLs
|
|
|
|
:rtype: ``list`` of :class:`CloudStackNetworkACLList`
|
|
"""
|
|
acllists = []
|
|
|
|
result = self._sync_request(command='listNetworkACLLists',
|
|
method='GET')
|
|
|
|
if not result:
|
|
return acllists
|
|
|
|
for acllist in result['networkacllist']:
|
|
acllists.append(CloudStackNetworkACLList(acllist['id'],
|
|
acllist['name'],
|
|
acllist.get('vpcid', []),
|
|
self,
|
|
acllist['description']))
|
|
|
|
return acllists
|
|
|
|
def ex_replace_network_acllist(self, acl_id, network_id):
|
|
"""
|
|
Create an ACL List for a network within a VPC.Replaces ACL associated
|
|
with a Network or private gateway
|
|
|
|
:param acl_id: the ID of the network ACL
|
|
:type acl_id: ``string``
|
|
|
|
:param network_id: the ID of the network
|
|
:type network_id: ``string``
|
|
|
|
:rtype: :class:`CloudStackNetworkACLList`
|
|
"""
|
|
|
|
args = {
|
|
'aclid': acl_id,
|
|
'networkid': network_id
|
|
}
|
|
|
|
self._async_request(command='replaceNetworkACLList',
|
|
params=args,
|
|
method='GET')
|
|
|
|
return True
|
|
|
|
def ex_list_network_acl(self):
|
|
"""
|
|
Lists all network ACL items
|
|
|
|
:rtype: ``list`` of :class:`CloudStackNetworkACL`
|
|
"""
|
|
acls = []
|
|
|
|
result = self._sync_request(command='listNetworkACLs',
|
|
method='GET')
|
|
|
|
if not result:
|
|
return acls
|
|
|
|
for acl in result['networkacl']:
|
|
acls.append(CloudStackNetworkACL(acl['id'],
|
|
acl['protocol'],
|
|
acl['aclid'],
|
|
acl['action'],
|
|
acl['cidrlist'],
|
|
acl.get('startport', []),
|
|
acl.get('endport', []),
|
|
acl['traffictype']))
|
|
|
|
return acls
|
|
|
|
def ex_list_keypairs(self, **kwargs):
|
|
"""
|
|
List Registered SSH Key Pairs
|
|
|
|
:param projectid: list objects by project
|
|
:type projectid: ``str``
|
|
|
|
:param page: The page to list the keypairs from
|
|
:type page: ``int``
|
|
|
|
:param keyword: List by keyword
|
|
:type keyword: ``str``
|
|
|
|
:param listall: If set to false, list only resources
|
|
belonging to the command's caller;
|
|
if set to true - list resources that
|
|
the caller is authorized to see.
|
|
Default value is false
|
|
|
|
:type listall: ``bool``
|
|
|
|
:param pagesize: The number of results per page
|
|
:type pagesize: ``int``
|
|
|
|
:param account: List resources by account.
|
|
Must be used with the domainId parameter
|
|
:type account: ``str``
|
|
|
|
:param isrecursive: Defaults to false, but if true,
|
|
lists all resources from
|
|
the parent specified by the
|
|
domainId till leaves.
|
|
:type isrecursive: ``bool``
|
|
|
|
:param fingerprint: A public key fingerprint to look for
|
|
:type fingerprint: ``str``
|
|
|
|
:param name: A key pair name to look for
|
|
:type name: ``str``
|
|
|
|
:param domainid: List only resources belonging to
|
|
the domain specified
|
|
:type domainid: ``str``
|
|
|
|
:return: A list of keypair dictionaries
|
|
:rtype: ``list`` of ``dict``
|
|
"""
|
|
warnings.warn('This method has been deprecated in favor of '
|
|
'list_key_pairs method')
|
|
|
|
key_pairs = self.list_key_pairs(**kwargs)
|
|
|
|
result = []
|
|
|
|
for key_pair in key_pairs:
|
|
item = {
|
|
'name': key_pair.name,
|
|
'fingerprint': key_pair.fingerprint,
|
|
'privateKey': key_pair.private_key
|
|
}
|
|
result.append(item)
|
|
|
|
return result
|
|
|
|
def ex_create_keypair(self, name, **kwargs):
|
|
"""
|
|
Creates a SSH KeyPair, returns fingerprint and private key
|
|
|
|
:param name: Name of the keypair (required)
|
|
:type name: ``str``
|
|
|
|
:param projectid: An optional project for the ssh key
|
|
:type projectid: ``str``
|
|
|
|
:param domainid: An optional domainId for the ssh key.
|
|
If the account parameter is used,
|
|
domainId must also be used.
|
|
:type domainid: ``str``
|
|
|
|
:param account: An optional account for the ssh key.
|
|
Must be used with domainId.
|
|
:type account: ``str``
|
|
|
|
:return: A keypair dictionary
|
|
:rtype: ``dict``
|
|
"""
|
|
warnings.warn('This method has been deprecated in favor of '
|
|
'create_key_pair method')
|
|
|
|
key_pair = self.create_key_pair(name=name, **kwargs)
|
|
|
|
result = {
|
|
'name': key_pair.name,
|
|
'fingerprint': key_pair.fingerprint,
|
|
'privateKey': key_pair.private_key
|
|
}
|
|
|
|
return result
|
|
|
|
def ex_import_keypair_from_string(self, name, key_material):
|
|
"""
|
|
Imports a new public key where the public key is passed in as a string
|
|
|
|
:param name: The name of the public key to import.
|
|
:type name: ``str``
|
|
|
|
:param key_material: The contents of a public key file.
|
|
:type key_material: ``str``
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
warnings.warn('This method has been deprecated in favor of '
|
|
'import_key_pair_from_string method')
|
|
|
|
key_pair = self.import_key_pair_from_string(name=name,
|
|
key_material=key_material)
|
|
result = {
|
|
'keyName': key_pair.name,
|
|
'keyFingerprint': key_pair.fingerprint
|
|
}
|
|
|
|
return result
|
|
|
|
def ex_import_keypair(self, name, keyfile):
|
|
"""
|
|
Imports a new public key where the public key is passed via a filename
|
|
|
|
:param name: The name of the public key to import.
|
|
:type name: ``str``
|
|
|
|
:param keyfile: The filename with path of the public key to import.
|
|
:type keyfile: ``str``
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
warnings.warn('This method has been deprecated in favor of '
|
|
'import_key_pair_from_file method')
|
|
|
|
key_pair = self.import_key_pair_from_file(name=name,
|
|
key_file_path=keyfile)
|
|
result = {
|
|
'keyName': key_pair.name,
|
|
'keyFingerprint': key_pair.fingerprint
|
|
}
|
|
|
|
return result
|
|
|
|
def ex_delete_keypair(self, keypair, **kwargs):
|
|
"""
|
|
Deletes an existing SSH KeyPair
|
|
|
|
:param keypair: Name of the keypair (required)
|
|
:type keypair: ``str``
|
|
|
|
:param projectid: The project associated with keypair
|
|
:type projectid: ``str``
|
|
|
|
:param domainid: The domain ID associated with the keypair
|
|
:type domainid: ``str``
|
|
|
|
:param account: The account associated with the keypair.
|
|
Must be used with the domainId parameter.
|
|
:type account: ``str``
|
|
|
|
:return: True of False based on success of Keypair deletion
|
|
:rtype: ``bool``
|
|
"""
|
|
warnings.warn('This method has been deprecated in favor of '
|
|
'delete_key_pair method')
|
|
|
|
key_pair = KeyPair(name=keypair, public_key=None, fingerprint=None,
|
|
driver=self)
|
|
|
|
return self.delete_key_pair(key_pair=key_pair)
|
|
|
|
def ex_list_security_groups(self, **kwargs):
|
|
"""
|
|
Lists Security Groups
|
|
|
|
:param domainid: List only resources belonging to the domain specified
|
|
:type domainid: ``str``
|
|
|
|
:param account: List resources by account. Must be used with
|
|
the domainId parameter.
|
|
:type account: ``str``
|
|
|
|
:param listall: If set to false, list only resources belonging to
|
|
the command's caller; if set to true
|
|
list resources that the caller is
|
|
authorized to see.
|
|
Default value is false
|
|
:type listall: ``bool``
|
|
|
|
:param pagesize: Number of entries per page
|
|
:type pagesize: ``int``
|
|
|
|
:param keyword: List by keyword
|
|
:type keyword: ``str``
|
|
|
|
:param tags: List resources by tags (key/value pairs)
|
|
:type tags: ``dict``
|
|
|
|
:param id: list the security group by the id provided
|
|
:type id: ``str``
|
|
|
|
:param securitygroupname: lists security groups by name
|
|
:type securitygroupname: ``str``
|
|
|
|
:param virtualmachineid: lists security groups by virtual machine id
|
|
:type virtualmachineid: ``str``
|
|
|
|
:param projectid: list objects by project
|
|
:type projectid: ``str``
|
|
|
|
:param isrecursive: (boolean) defaults to false, but if true,
|
|
lists all resources from the parent
|
|
specified by the domainId till leaves.
|
|
:type isrecursive: ``bool``
|
|
|
|
:param page: (integer)
|
|
:type page: ``int``
|
|
|
|
:rtype ``list``
|
|
"""
|
|
extra_args = kwargs.copy()
|
|
res = self._sync_request(command='listSecurityGroups',
|
|
params=extra_args,
|
|
method='GET')
|
|
|
|
security_groups = res.get('securitygroup', [])
|
|
return security_groups
|
|
|
|
def ex_create_security_group(self, name, **kwargs):
|
|
"""
|
|
Creates a new Security Group
|
|
|
|
:param name: name of the security group (required)
|
|
:type name: ``str``
|
|
|
|
:param account: An optional account for the security group.
|
|
Must be used with domainId.
|
|
:type account: ``str``
|
|
|
|
:param domainid: An optional domainId for the security group.
|
|
If the account parameter is used,
|
|
domainId must also be used.
|
|
:type domainid: ``str``
|
|
|
|
:param description: The description of the security group
|
|
:type description: ``str``
|
|
|
|
:param projectid: Deploy vm for the project
|
|
:type projectid: ``str``
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
|
|
extra_args = kwargs.copy()
|
|
|
|
for sg in self.ex_list_security_groups():
|
|
if name in sg['name']:
|
|
raise LibcloudError('This Security Group name already exists')
|
|
|
|
params = {'name': name}
|
|
params.update(extra_args)
|
|
|
|
return self._sync_request(command='createSecurityGroup',
|
|
params=params,
|
|
method='GET')['securitygroup']
|
|
|
|
def ex_delete_security_group(self, name):
|
|
"""
|
|
Deletes a given Security Group
|
|
|
|
:param domainid: The domain ID of account owning
|
|
the security group
|
|
:type domainid: ``str``
|
|
|
|
:param id: The ID of the security group.
|
|
Mutually exclusive with name parameter
|
|
:type id: ``str``
|
|
|
|
:param name: The ID of the security group.
|
|
Mutually exclusive with id parameter
|
|
:type name: ``str``
|
|
|
|
:param account: The account of the security group.
|
|
Must be specified with domain ID
|
|
:type account: ``str``
|
|
|
|
:param projectid: The project of the security group
|
|
:type projectid: ``str``
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
return self._sync_request(command='deleteSecurityGroup',
|
|
params={'name': name},
|
|
method='GET')['success']
|
|
|
|
def ex_authorize_security_group_ingress(self, securitygroupname, protocol,
|
|
cidrlist, startport=None,
|
|
endport=None, icmptype=None,
|
|
icmpcode=None, **kwargs):
|
|
"""
|
|
Creates a new Security Group Ingress rule
|
|
|
|
:param securitygroupname: The name of the security group.
|
|
Mutually exclusive with securitygroupid.
|
|
:type securitygroupname: ``str``
|
|
|
|
:param protocol: Can be TCP, UDP or ICMP.
|
|
Sometime other protocols can be used like AH, ESP
|
|
or GRE.
|
|
:type protocol: ``str``
|
|
|
|
:param cidrlist: Source address CIDR for which this rule applies.
|
|
:type cidrlist: ``str``
|
|
|
|
:param startport: Start port of the range for this ingress rule.
|
|
Applies to protocols TCP and UDP.
|
|
:type startport: ``int``
|
|
|
|
:param endport: End port of the range for this ingress rule.
|
|
It can be None to set only one port.
|
|
Applies to protocols TCP and UDP.
|
|
:type endport: ``int``
|
|
|
|
:param icmptype: Type of the ICMP packet (eg: 8 for Echo Request).
|
|
-1 or None means "all types".
|
|
Applies to protocol ICMP.
|
|
:type icmptype: ``int``
|
|
|
|
:param icmpcode: Code of the ICMP packet for the specified type.
|
|
If the specified type doesn't require a code set
|
|
this value to 0.
|
|
-1 or None means "all codes".
|
|
Applies to protocol ICMP.
|
|
:type icmpcode: ``int``
|
|
|
|
:keyword account: An optional account for the security group.
|
|
Must be used with domainId.
|
|
:type account: ``str``
|
|
|
|
:keyword domainid: An optional domainId for the security group.
|
|
If the account parameter is used, domainId must also
|
|
be used.
|
|
|
|
:keyword projectid: An optional project of the security group
|
|
:type projectid: ``str``
|
|
|
|
:keyword securitygroupid: The ID of the security group.
|
|
Mutually exclusive with securitygroupname
|
|
:type securitygroupid: ``str``
|
|
|
|
:keyword usersecuritygrouplist: User to security group mapping
|
|
:type usersecuritygrouplist: ``dict``
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
|
|
args = kwargs.copy()
|
|
protocol = protocol.upper()
|
|
|
|
args.update({
|
|
'securitygroupname': securitygroupname,
|
|
'protocol': protocol,
|
|
'cidrlist': cidrlist
|
|
})
|
|
|
|
if protocol not in ('TCP', 'UDP') and \
|
|
(startport is not None or endport is not None):
|
|
raise LibcloudError('"startport" and "endport" are only valid '
|
|
'with protocol TCP or UDP.')
|
|
|
|
if protocol != 'ICMP' and \
|
|
(icmptype is not None or icmpcode is not None):
|
|
raise LibcloudError('"icmptype" and "icmpcode" are only valid '
|
|
'with protocol ICMP.')
|
|
|
|
if protocol in ('TCP', 'UDP'):
|
|
if startport is None:
|
|
raise LibcloudError('Protocols TCP and UDP require at least '
|
|
'"startport" to be set.')
|
|
if startport is not None and endport is None:
|
|
endport = startport
|
|
|
|
args.update({
|
|
'startport': startport,
|
|
'endport': endport
|
|
})
|
|
|
|
if protocol == 'ICMP':
|
|
if icmptype is None:
|
|
icmptype = -1
|
|
if icmpcode is None:
|
|
icmpcode = -1
|
|
|
|
args.update({
|
|
'icmptype': icmptype,
|
|
'icmpcode': icmpcode
|
|
})
|
|
|
|
return self._async_request(command='authorizeSecurityGroupIngress',
|
|
params=args,
|
|
method='GET')['securitygroup']
|
|
|
|
def ex_revoke_security_group_ingress(self, rule_id):
|
|
"""
|
|
Revoke/delete an ingress security rule
|
|
|
|
:param id: The ID of the ingress security rule
|
|
:type id: ``str``
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
self._async_request(command='revokeSecurityGroupIngress',
|
|
params={'id': rule_id},
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_create_affinity_group(self, name, group_type):
|
|
"""
|
|
Creates a new Affinity Group
|
|
|
|
:param name: Name of the affinity group
|
|
:type name: ``str``
|
|
|
|
:param group_type: Type of the affinity group from the available
|
|
affinity/anti-affinity group types
|
|
:type group_type: :class:`CloudStackAffinityGroupType`
|
|
|
|
:param description: Optional description of the affinity group
|
|
:type description: ``str``
|
|
|
|
:param domainid: domain ID of the account owning the affinity group
|
|
:type domainid: ``str``
|
|
|
|
:rtype: :class:`CloudStackAffinityGroup`
|
|
"""
|
|
|
|
for ag in self.ex_list_affinity_groups():
|
|
if name == ag.name:
|
|
raise LibcloudError('This Affinity Group name already exists')
|
|
|
|
params = {'name': name, 'type': group_type.type}
|
|
|
|
result = self._async_request(command='createAffinityGroup',
|
|
params=params,
|
|
method='GET')
|
|
|
|
return self._to_affinity_group(result['affinitygroup'])
|
|
|
|
def ex_delete_affinity_group(self, affinity_group):
|
|
"""
|
|
Delete an Affinity Group
|
|
|
|
:param affinity_group: Instance of affinity group
|
|
:type affinity_group: :class:`CloudStackAffinityGroup`
|
|
|
|
:rtype ``bool``
|
|
"""
|
|
return self._async_request(command='deleteAffinityGroup',
|
|
params={'id': affinity_group.id},
|
|
method='GET')['success']
|
|
|
|
def ex_update_node_affinity_group(self, node, affinity_group_list):
|
|
"""
|
|
Updates the affinity/anti-affinity group associations of a virtual
|
|
machine. The VM has to be stopped and restarted for the new properties
|
|
to take effect.
|
|
|
|
:param node: Node to update.
|
|
:type node: :class:`CloudStackNode`
|
|
|
|
:param affinity_group_list: List of CloudStackAffinityGroup to
|
|
associate
|
|
:type affinity_group_list: ``list`` of :class:`CloudStackAffinityGroup`
|
|
|
|
:rtype :class:`CloudStackNode`
|
|
"""
|
|
affinity_groups = ','.join(ag.id for ag in affinity_group_list)
|
|
|
|
result = self._async_request(command='updateVMAffinityGroup',
|
|
params={
|
|
'id': node.id,
|
|
'affinitygroupids': affinity_groups},
|
|
method='GET')
|
|
return self._to_node(data=result['virtualmachine'])
|
|
|
|
def ex_list_affinity_groups(self):
|
|
"""
|
|
List Affinity Groups
|
|
|
|
:rtype ``list`` of :class:`CloudStackAffinityGroup`
|
|
"""
|
|
result = self._sync_request(command='listAffinityGroups', method='GET')
|
|
|
|
if not result.get('count'):
|
|
return []
|
|
|
|
affinity_groups = []
|
|
for ag in result['affinitygroup']:
|
|
affinity_groups.append(self._to_affinity_group(ag))
|
|
|
|
return affinity_groups
|
|
|
|
def ex_list_affinity_group_types(self):
|
|
"""
|
|
List Affinity Group Types
|
|
|
|
:rtype ``list`` of :class:`CloudStackAffinityGroupTypes`
|
|
"""
|
|
result = self._sync_request(command='listAffinityGroupTypes',
|
|
method='GET')
|
|
|
|
if not result.get('count'):
|
|
return []
|
|
|
|
affinity_group_types = []
|
|
for agt in result['affinityGroupType']:
|
|
affinity_group_types.append(
|
|
CloudStackAffinityGroupType(agt['type']))
|
|
|
|
return affinity_group_types
|
|
|
|
def ex_register_iso(self, name, url, location=None, **kwargs):
|
|
"""
|
|
Registers an existing ISO by URL.
|
|
|
|
:param name: Name which should be used
|
|
:type name: ``str``
|
|
|
|
:param url: Url should be used
|
|
:type url: ``str``
|
|
|
|
:param location: Location which should be used
|
|
:type location: :class:`NodeLocation`
|
|
|
|
:rtype: ``str``
|
|
"""
|
|
if location is None:
|
|
location = self.list_locations()[0]
|
|
|
|
params = {'name': name,
|
|
'displaytext': name,
|
|
'url': url,
|
|
'zoneid': location.id}
|
|
params['bootable'] = kwargs.pop('bootable', False)
|
|
if params['bootable']:
|
|
os_type_id = kwargs.pop('ostypeid', None)
|
|
|
|
if not os_type_id:
|
|
raise LibcloudError('If bootable=True, ostypeid is required!')
|
|
|
|
params['ostypeid'] = os_type_id
|
|
|
|
return self._sync_request(command='registerIso',
|
|
params=params)
|
|
|
|
def ex_limits(self):
|
|
"""
|
|
Extra call to get account's resource limits, such as
|
|
the amount of instances, volumes, snapshots and networks.
|
|
|
|
CloudStack uses integers as the resource type so we will convert
|
|
them to a more human readable string using the resource map
|
|
|
|
A list of the resource type mappings can be found at
|
|
http://goo.gl/17C6Gk
|
|
|
|
:return: dict
|
|
:rtype: ``dict``
|
|
"""
|
|
|
|
result = self._sync_request(command='listResourceLimits',
|
|
method='GET')
|
|
|
|
limits = {}
|
|
resource_map = {
|
|
0: 'max_instances',
|
|
1: 'max_public_ips',
|
|
2: 'max_volumes',
|
|
3: 'max_snapshots',
|
|
4: 'max_images',
|
|
5: 'max_projects',
|
|
6: 'max_networks',
|
|
7: 'max_vpc',
|
|
8: 'max_cpu',
|
|
9: 'max_memory',
|
|
10: 'max_primary_storage',
|
|
11: 'max_secondary_storage'
|
|
}
|
|
|
|
for limit in result.get('resourcelimit', []):
|
|
# We will ignore unknown types
|
|
resource = resource_map.get(int(limit['resourcetype']), None)
|
|
if not resource:
|
|
continue
|
|
limits[resource] = int(limit['max'])
|
|
|
|
return limits
|
|
|
|
def ex_create_tags(self, resource_ids, resource_type, tags):
|
|
"""
|
|
Create tags for a resource (Node/StorageVolume/etc).
|
|
A list of resource types can be found at http://goo.gl/6OKphH
|
|
|
|
:param resource_ids: Resource IDs to be tagged. The resource IDs must
|
|
all be associated with the resource_type.
|
|
For example, for virtual machines (UserVm) you
|
|
can only specify a list of virtual machine IDs.
|
|
:type resource_ids: ``list`` of resource IDs
|
|
|
|
:param resource_type: Resource type (eg: UserVm)
|
|
:type resource_type: ``str``
|
|
|
|
:param tags: A dictionary or other mapping of strings to strings,
|
|
associating tag names with tag values.
|
|
:type tags: ``dict``
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
params = {'resourcetype': resource_type,
|
|
'resourceids': ','.join(resource_ids)}
|
|
|
|
for i, key in enumerate(tags):
|
|
params['tags[%d].key' % i] = key
|
|
params['tags[%d].value' % i] = tags[key]
|
|
|
|
self._async_request(command='createTags',
|
|
params=params,
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_delete_tags(self, resource_ids, resource_type, tag_keys):
|
|
"""
|
|
Delete tags from a resource.
|
|
|
|
:param resource_ids: Resource IDs to be tagged. The resource IDs must
|
|
all be associated with the resource_type.
|
|
For example, for virtual machines (UserVm) you
|
|
can only specify a list of virtual machine IDs.
|
|
:type resource_ids: ``list`` of resource IDs
|
|
|
|
:param resource_type: Resource type (eg: UserVm)
|
|
:type resource_type: ``str``
|
|
|
|
:param tag_keys: A list of keys to delete. CloudStack only requires
|
|
the keys from the key/value pair.
|
|
:type tag_keys: ``list``
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
params = {'resourcetype': resource_type,
|
|
'resourceids': ','.join(resource_ids)}
|
|
|
|
for i, key in enumerate(tag_keys):
|
|
params['tags[%s].key' % i] = key
|
|
|
|
self._async_request(command='deleteTags',
|
|
params=params,
|
|
method='GET')
|
|
|
|
return True
|
|
|
|
def list_snapshots(self):
|
|
"""
|
|
Describe all snapshots.
|
|
|
|
:rtype: ``list`` of :class:`VolumeSnapshot`
|
|
"""
|
|
snapshots = self._sync_request('listSnapshots',
|
|
method='GET')
|
|
list_snapshots = []
|
|
|
|
for snap in snapshots['snapshot']:
|
|
list_snapshots.append(self._to_snapshot(snap))
|
|
return list_snapshots
|
|
|
|
def create_volume_snapshot(self, volume, name=None):
|
|
"""
|
|
Create snapshot from volume
|
|
|
|
:param volume: Instance of ``StorageVolume``
|
|
:type volume: ``StorageVolume``
|
|
|
|
:param name: The name of the snapshot is disregarded
|
|
by CloudStack drivers
|
|
:type name: `str`
|
|
|
|
:rtype: :class:`VolumeSnapshot`
|
|
"""
|
|
snapshot = self._async_request(command='createSnapshot',
|
|
params={'volumeid': volume.id},
|
|
method='GET')
|
|
return self._to_snapshot(snapshot['snapshot'])
|
|
|
|
def destroy_volume_snapshot(self, snapshot):
|
|
self._async_request(command='deleteSnapshot',
|
|
params={'id': snapshot.id},
|
|
method='GET')
|
|
return True
|
|
|
|
def ex_create_snapshot_template(self, snapshot, name, ostypeid,
|
|
displaytext=None):
|
|
"""
|
|
Create a template from a snapshot
|
|
|
|
:param snapshot: Instance of ``VolumeSnapshot``
|
|
:type volume: ``VolumeSnapshot``
|
|
|
|
:param name: the name of the template
|
|
:type name: ``str``
|
|
|
|
:param name: the os type id
|
|
:type name: ``str``
|
|
|
|
:param name: the display name of the template
|
|
:type name: ``str``
|
|
|
|
:rtype: :class:`NodeImage`
|
|
"""
|
|
if not displaytext:
|
|
displaytext = name
|
|
resp = self._async_request(command='createTemplate',
|
|
params={
|
|
'displaytext': displaytext,
|
|
'name': name,
|
|
'ostypeid': ostypeid,
|
|
'snapshotid': snapshot.id})
|
|
img = resp.get('template')
|
|
extra = {
|
|
'hypervisor': img['hypervisor'],
|
|
'format': img['format'],
|
|
'os': img['ostypename'],
|
|
'displaytext': img['displaytext']
|
|
}
|
|
return NodeImage(id=img['id'],
|
|
name=img['name'],
|
|
driver=self.connection.driver,
|
|
extra=extra)
|
|
|
|
def ex_list_os_types(self):
|
|
"""
|
|
List all registered os types (needed for snapshot creation)
|
|
|
|
:rtype: ``list``
|
|
"""
|
|
ostypes = self._sync_request('listOsTypes')
|
|
return ostypes['ostype']
|
|
|
|
def ex_list_nics(self, node):
|
|
"""
|
|
List the available networks
|
|
|
|
:param vm: Node Object
|
|
:type vm: :class:`CloudStackNode
|
|
|
|
:rtype ``list`` of :class:`CloudStackNic`
|
|
"""
|
|
|
|
res = self._sync_request(command='listNics',
|
|
params={'virtualmachineid': node.id},
|
|
method='GET')
|
|
items = res.get('nic', [])
|
|
|
|
nics = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['nic']
|
|
for item in items:
|
|
extra = self._get_extra_dict(item, extra_map)
|
|
|
|
nics.append(CloudStackNic(
|
|
id=item['id'],
|
|
network_id=item['networkid'],
|
|
net_mask=item['netmask'],
|
|
gateway=item['gateway'],
|
|
ip_address=item['ipaddress'],
|
|
is_default=item['isdefault'],
|
|
mac_address=item['macaddress'],
|
|
driver=self,
|
|
extra=extra))
|
|
|
|
return nics
|
|
|
|
def ex_attach_nic_to_node(self, node, network, ip_address=None):
|
|
"""
|
|
Add an extra Nic to a VM
|
|
|
|
:param network: NetworkOffering object
|
|
:type network: :class:'CloudStackNetwork`
|
|
|
|
:param node: Node Object
|
|
:type node: :class:'CloudStackNode`
|
|
|
|
:param ip_address: Optional, specific IP for this Nic
|
|
:type ip_address: ``str``
|
|
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
args = {
|
|
'virtualmachineid': node.id,
|
|
'networkid': network.id
|
|
}
|
|
|
|
if ip_address is not None:
|
|
args['ipaddress'] = ip_address
|
|
|
|
self._async_request(command='addNicToVirtualMachine',
|
|
params=args)
|
|
return True
|
|
|
|
def ex_detach_nic_from_node(self, nic, node):
|
|
|
|
"""
|
|
Remove Nic from a VM
|
|
|
|
:param nic: Nic object
|
|
:type nic: :class:'CloudStackNetwork`
|
|
|
|
:param node: Node Object
|
|
:type node: :class:'CloudStackNode`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
|
|
self._async_request(command='removeNicFromVirtualMachine',
|
|
params={'nicid': nic.id,
|
|
'virtualmachineid': node.id})
|
|
|
|
return True
|
|
|
|
def ex_list_vpn_gateways(self, account=None, domain_id=None,
|
|
for_display=None, id=None, is_recursive=None,
|
|
keyword=None, list_all=None, page=None,
|
|
page_size=None, project_id=None, vpc_id=None):
|
|
"""
|
|
List VPN Gateways.
|
|
|
|
:param account: List resources by account (must be
|
|
used with the domain_id parameter).
|
|
:type account: ``str``
|
|
|
|
:param domain_id: List only resources belonging
|
|
to the domain specified.
|
|
:type domain_id: ``str``
|
|
|
|
:param for_display: List resources by display flag (only root
|
|
admin is eligible to pass this parameter).
|
|
:type for_display: ``bool``
|
|
|
|
:param id: ID of the VPN Gateway.
|
|
:type id: ``str``
|
|
|
|
:param is_recursive: Defaults to False, but if true, lists all
|
|
resources from the parent specified by the
|
|
domain ID till leaves.
|
|
:type is_recursive: ``bool``
|
|
|
|
:param keyword: List by keyword.
|
|
:type keyword: ``str``
|
|
|
|
:param list_all: If set to False, list only resources belonging to
|
|
the command's caller; if set to True - list
|
|
resources that the caller is authorized to see.
|
|
Default value is False.
|
|
:type list_all: ``str``
|
|
|
|
:param page: Start from page.
|
|
:type page: ``int``
|
|
|
|
:param page_size: Items per page.
|
|
:type page_size: ``int``
|
|
|
|
:param project_id: List objects by project.
|
|
:type project_id: ``str``
|
|
|
|
:param vpc_id: List objects by VPC.
|
|
:type vpc_id: ``str``
|
|
|
|
:rtype: ``list`` of :class:`CloudStackVpnGateway`
|
|
"""
|
|
args = {}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if for_display is not None:
|
|
args['fordisplay'] = for_display
|
|
|
|
if id is not None:
|
|
args['id'] = id
|
|
|
|
if is_recursive is not None:
|
|
args['isrecursive'] = is_recursive
|
|
|
|
if keyword is not None:
|
|
args['keyword'] = keyword
|
|
|
|
if list_all is not None:
|
|
args['listall'] = list_all
|
|
|
|
if page is not None:
|
|
args['page'] = page
|
|
|
|
if page_size is not None:
|
|
args['pagesize'] = page_size
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
if vpc_id is not None:
|
|
args['vpcid'] = vpc_id
|
|
|
|
res = self._sync_request(command='listVpnGateways',
|
|
params=args,
|
|
method='GET')
|
|
|
|
items = res.get('vpngateway', [])
|
|
vpn_gateways = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpngateway']
|
|
|
|
for item in items:
|
|
extra = self._get_extra_dict(item, extra_map)
|
|
|
|
vpn_gateways.append(CloudStackVpnGateway(
|
|
id=item['id'],
|
|
account=item['account'],
|
|
domain=item['domain'],
|
|
domain_id=item['domainid'],
|
|
public_ip=item['publicip'],
|
|
vpc_id=item['vpcid'],
|
|
driver=self,
|
|
extra=extra))
|
|
|
|
return vpn_gateways
|
|
|
|
def ex_create_vpn_gateway(self, vpc, for_display=None):
|
|
"""
|
|
Creates a VPN Gateway.
|
|
|
|
:param vpc: VPC to create the Gateway for (required).
|
|
:type vpc: :class: `CloudStackVPC`
|
|
|
|
:param for_display: Display the VPC to the end user or not.
|
|
:type for_display: ``bool``
|
|
|
|
:rtype: :class: `CloudStackVpnGateway`
|
|
"""
|
|
args = {
|
|
'vpcid': vpc.id,
|
|
}
|
|
|
|
if for_display is not None:
|
|
args['fordisplay'] = for_display
|
|
|
|
res = self._async_request(command='createVpnGateway',
|
|
params=args,
|
|
method='GET')
|
|
|
|
item = res['vpngateway']
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpngateway']
|
|
|
|
return CloudStackVpnGateway(id=item['id'],
|
|
account=item['account'],
|
|
domain=item['domain'],
|
|
domain_id=item['domainid'],
|
|
public_ip=item['publicip'],
|
|
vpc_id=vpc.id,
|
|
driver=self,
|
|
extra=self._get_extra_dict(item,
|
|
extra_map))
|
|
|
|
def ex_delete_vpn_gateway(self, vpn_gateway):
|
|
"""
|
|
Deletes a VPN Gateway.
|
|
|
|
:param vpn_gateway: The VPN Gateway (required).
|
|
:type vpn_gateway: :class:`CloudStackVpnGateway`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='deleteVpnGateway',
|
|
params={'id': vpn_gateway.id},
|
|
method='GET')
|
|
|
|
return res['success']
|
|
|
|
def ex_list_vpn_customer_gateways(self, account=None, domain_id=None,
|
|
id=None, is_recursive=None,
|
|
keyword=None, list_all=None,
|
|
page=None, page_size=None,
|
|
project_id=None):
|
|
"""
|
|
List VPN Customer Gateways.
|
|
|
|
:param account: List resources by account (must be
|
|
used with the domain_id parameter).
|
|
:type account: ``str``
|
|
|
|
:param domain_id: List only resources belonging
|
|
to the domain specified.
|
|
:type domain_id: ``str``
|
|
|
|
:param id: ID of the VPN Customer Gateway.
|
|
:type id: ``str``
|
|
|
|
:param is_recursive: Defaults to False, but if true, lists all
|
|
resources from the parent specified by the
|
|
domain_id till leaves.
|
|
:type is_recursive: ``bool``
|
|
|
|
:param keyword: List by keyword.
|
|
:type keyword: ``str``
|
|
|
|
:param list_all: If set to False, list only resources belonging to
|
|
the command's caller; if set to True - list
|
|
resources that the caller is authorized to see.
|
|
Default value is False.
|
|
:type list_all: ``str``
|
|
|
|
:param page: Start from page.
|
|
:type page: ``int``
|
|
|
|
:param page_size: Items per page.
|
|
:type page_size: ``int``
|
|
|
|
:param project_id: List objects by project.
|
|
:type project_id: ``str``
|
|
|
|
:rtype: ``list`` of :class:`CloudStackVpnCustomerGateway`
|
|
"""
|
|
args = {}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if id is not None:
|
|
args['id'] = id
|
|
|
|
if is_recursive is not None:
|
|
args['isrecursive'] = is_recursive
|
|
|
|
if keyword is not None:
|
|
args['keyword'] = keyword
|
|
|
|
if list_all is not None:
|
|
args['listall'] = list_all
|
|
|
|
if page is not None:
|
|
args['page'] = page
|
|
|
|
if page_size is not None:
|
|
args['pagesize'] = page_size
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
res = self._sync_request(command='listVpnCustomerGateways',
|
|
params=args,
|
|
method='GET')
|
|
|
|
items = res.get('vpncustomergateway', [])
|
|
vpn_customer_gateways = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpncustomergateway']
|
|
|
|
for item in items:
|
|
extra = self._get_extra_dict(item, extra_map)
|
|
|
|
vpn_customer_gateways.append(CloudStackVpnCustomerGateway(
|
|
id=item['id'],
|
|
cidr_list=item['cidrlist'],
|
|
esp_policy=item['esppolicy'],
|
|
gateway=item['gateway'],
|
|
ike_policy=item['ikepolicy'],
|
|
ipsec_psk=item['ipsecpsk'],
|
|
driver=self,
|
|
extra=extra))
|
|
|
|
return vpn_customer_gateways
|
|
|
|
def ex_create_vpn_customer_gateway(self, cidr_list, esp_policy, gateway,
|
|
ike_policy, ipsec_psk, account=None,
|
|
domain_id=None, dpd=None,
|
|
esp_lifetime=None, ike_lifetime=None,
|
|
name=None):
|
|
"""
|
|
Creates a VPN Customer Gateway.
|
|
|
|
:param cidr_list: Guest CIDR list of the Customer Gateway (required).
|
|
:type cidr_list: ``str``
|
|
|
|
:param esp_policy: ESP policy of the Customer Gateway (required).
|
|
:type esp_policy: ``str``
|
|
|
|
:param gateway: Public IP address of the Customer Gateway (required).
|
|
:type gateway: ``str``
|
|
|
|
:param ike_policy: IKE policy of the Customer Gateway (required).
|
|
:type ike_policy: ``str``
|
|
|
|
:param ipsec_psk: IPsec preshared-key of the Customer Gateway
|
|
(required).
|
|
:type ipsec_psk: ``str``
|
|
|
|
:param account: The associated account with the Customer Gateway
|
|
(must be used with the domain_id param).
|
|
:type account: ``str``
|
|
|
|
:param domain_id: The domain ID associated with the Customer Gateway.
|
|
If used with the account parameter returns the
|
|
gateway associated with the account for the
|
|
specified domain.
|
|
:type domain_id: ``str``
|
|
|
|
:param dpd: If DPD is enabled for the VPN connection.
|
|
:type dpd: ``bool``
|
|
|
|
:param esp_lifetime: Lifetime of phase 2 VPN connection to the
|
|
Customer Gateway, in seconds.
|
|
:type esp_lifetime: ``int``
|
|
|
|
:param ike_lifetime: Lifetime of phase 1 VPN connection to the
|
|
Customer Gateway, in seconds.
|
|
:type ike_lifetime: ``int``
|
|
|
|
:param name: Name of the Customer Gateway.
|
|
:type name: ``str``
|
|
|
|
:rtype: :class: `CloudStackVpnCustomerGateway`
|
|
"""
|
|
args = {
|
|
'cidrlist': cidr_list,
|
|
'esppolicy': esp_policy,
|
|
'gateway': gateway,
|
|
'ikepolicy': ike_policy,
|
|
'ipsecpsk': ipsec_psk
|
|
}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if dpd is not None:
|
|
args['dpd'] = dpd
|
|
|
|
if esp_lifetime is not None:
|
|
args['esplifetime'] = esp_lifetime
|
|
|
|
if ike_lifetime is not None:
|
|
args['ikelifetime'] = ike_lifetime
|
|
|
|
if name is not None:
|
|
args['name'] = name
|
|
|
|
res = self._async_request(command='createVpnCustomerGateway',
|
|
params=args,
|
|
method='GET')
|
|
|
|
item = res['vpncustomergateway']
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpncustomergateway']
|
|
|
|
return CloudStackVpnCustomerGateway(id=item['id'],
|
|
cidr_list=cidr_list,
|
|
esp_policy=esp_policy,
|
|
gateway=gateway,
|
|
ike_policy=ike_policy,
|
|
ipsec_psk=ipsec_psk,
|
|
driver=self,
|
|
extra=self._get_extra_dict(
|
|
item, extra_map))
|
|
|
|
def ex_delete_vpn_customer_gateway(self, vpn_customer_gateway):
|
|
"""
|
|
Deletes a VPN Customer Gateway.
|
|
|
|
:param vpn_customer_gateway: The VPN Customer Gateway (required).
|
|
:type vpn_customer_gateway: :class:`CloudStackVpnCustomerGateway`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='deleteVpnCustomerGateway',
|
|
params={'id': vpn_customer_gateway.id},
|
|
method='GET')
|
|
|
|
return res['success']
|
|
|
|
def ex_list_vpn_connections(self, account=None, domain_id=None,
|
|
for_display=None, id=None, is_recursive=None,
|
|
keyword=None, list_all=None, page=None,
|
|
page_size=None, project_id=None, vpc_id=None):
|
|
"""
|
|
List VPN Connections.
|
|
|
|
:param account: List resources by account (must be
|
|
used with the domain_id parameter).
|
|
:type account: ``str``
|
|
|
|
:param domain_id: List only resources belonging
|
|
to the domain specified.
|
|
:type domain_id: ``str``
|
|
|
|
:param for_display: List resources by display flag (only root
|
|
admin is eligible to pass this parameter).
|
|
:type for_display: ``bool``
|
|
|
|
:param id: ID of the VPN Connection.
|
|
:type id: ``str``
|
|
|
|
:param is_recursive: Defaults to False, but if true, lists all
|
|
resources from the parent specified by the
|
|
domain_id till leaves.
|
|
:type is_recursive: ``bool``
|
|
|
|
:param keyword: List by keyword.
|
|
:type keyword: ``str``
|
|
|
|
:param list_all: If set to False, list only resources belonging to
|
|
the command's caller; if set to True - list
|
|
resources that the caller is authorized to see.
|
|
Default value is False.
|
|
:type list_all: ``str``
|
|
|
|
:param page: Start from page.
|
|
:type page: ``int``
|
|
|
|
:param page_size: Items per page.
|
|
:type page_size: ``int``
|
|
|
|
:param project_id: List objects by project.
|
|
:type project_id: ``str``
|
|
|
|
:param vpc_id: List objects by VPC.
|
|
:type vpc_id: ``str``
|
|
|
|
:rtype: ``list`` of :class:`CloudStackVpnConnection`
|
|
"""
|
|
args = {}
|
|
|
|
if account is not None:
|
|
args['account'] = account
|
|
|
|
if domain_id is not None:
|
|
args['domainid'] = domain_id
|
|
|
|
if for_display is not None:
|
|
args['fordisplay'] = for_display
|
|
|
|
if id is not None:
|
|
args['id'] = id
|
|
|
|
if is_recursive is not None:
|
|
args['isrecursive'] = is_recursive
|
|
|
|
if keyword is not None:
|
|
args['keyword'] = keyword
|
|
|
|
if list_all is not None:
|
|
args['listall'] = list_all
|
|
|
|
if page is not None:
|
|
args['page'] = page
|
|
|
|
if page_size is not None:
|
|
args['pagesize'] = page_size
|
|
|
|
if project_id is not None:
|
|
args['projectid'] = project_id
|
|
|
|
if vpc_id is not None:
|
|
args['vpcid'] = vpc_id
|
|
|
|
res = self._sync_request(command='listVpnConnections',
|
|
params=args,
|
|
method='GET')
|
|
|
|
items = res.get('vpnconnection', [])
|
|
vpn_connections = []
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpnconnection']
|
|
|
|
for item in items:
|
|
extra = self._get_extra_dict(item, extra_map)
|
|
|
|
vpn_connections.append(CloudStackVpnConnection(
|
|
id=item['id'],
|
|
passive=item['passive'],
|
|
vpn_customer_gateway_id=item['s2scustomergatewayid'],
|
|
vpn_gateway_id=item['s2svpngatewayid'],
|
|
state=item['state'],
|
|
driver=self,
|
|
extra=extra))
|
|
|
|
return vpn_connections
|
|
|
|
def ex_create_vpn_connection(self, vpn_customer_gateway, vpn_gateway,
|
|
for_display=None, passive=None):
|
|
"""
|
|
Creates a VPN Connection.
|
|
|
|
:param vpn_customer_gateway: The VPN Customer Gateway (required).
|
|
:type vpn_customer_gateway: :class:`CloudStackVpnCustomerGateway`
|
|
|
|
:param vpn_gateway: The VPN Gateway (required).
|
|
:type vpn_gateway: :class:`CloudStackVpnGateway`
|
|
|
|
:param for_display: Display the Connection to the end user or not.
|
|
:type for_display: ``str``
|
|
|
|
:param passive: If True, sets the connection to be passive.
|
|
:type passive: ``bool``
|
|
|
|
:rtype: :class: `CloudStackVpnConnection`
|
|
"""
|
|
args = {
|
|
's2scustomergatewayid': vpn_customer_gateway.id,
|
|
's2svpngatewayid': vpn_gateway.id,
|
|
}
|
|
|
|
if for_display is not None:
|
|
args['fordisplay'] = for_display
|
|
|
|
if passive is not None:
|
|
args['passive'] = passive
|
|
|
|
res = self._async_request(command='createVpnConnection',
|
|
params=args,
|
|
method='GET')
|
|
|
|
item = res['vpnconnection']
|
|
extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['vpnconnection']
|
|
|
|
return CloudStackVpnConnection(
|
|
id=item['id'],
|
|
passive=item['passive'],
|
|
vpn_customer_gateway_id=vpn_customer_gateway.id,
|
|
vpn_gateway_id=vpn_gateway.id,
|
|
state=item['state'],
|
|
driver=self,
|
|
extra=self._get_extra_dict(item, extra_map))
|
|
|
|
def ex_delete_vpn_connection(self, vpn_connection):
|
|
"""
|
|
Deletes a VPN Connection.
|
|
|
|
:param vpn_connection: The VPN Connection (required).
|
|
:type vpn_connection: :class:`CloudStackVpnConnection`
|
|
|
|
:rtype: ``bool``
|
|
"""
|
|
res = self._async_request(command='deleteVpnConnection',
|
|
params={'id': vpn_connection.id},
|
|
method='GET')
|
|
|
|
return res['success']
|
|
|
|
def _to_snapshot(self, data):
|
|
"""
|
|
Create snapshot object from data
|
|
|
|
:param data: Node data object.
|
|
:type data: ``dict``
|
|
|
|
:rtype: :class:`VolumeSnapshot`
|
|
"""
|
|
extra = {
|
|
'tags': data.get('tags', None),
|
|
'name': data.get('name', None),
|
|
'volume_id': data.get('volumeid', None),
|
|
}
|
|
return VolumeSnapshot(data['id'], driver=self, extra=extra)
|
|
|
|
def _to_node(self, data, public_ips=None):
|
|
"""
|
|
:param data: Node data object.
|
|
:type data: ``dict``
|
|
|
|
:param public_ips: A list of additional IP addresses belonging to
|
|
this node. (optional)
|
|
:type public_ips: ``list`` or ``None``
|
|
"""
|
|
id = data['id']
|
|
|
|
if 'name' in data:
|
|
name = data['name']
|
|
elif 'displayname' in data:
|
|
name = data['displayname']
|
|
else:
|
|
name = None
|
|
|
|
state = self.NODE_STATE_MAP[data['state']]
|
|
|
|
public_ips = public_ips if public_ips else []
|
|
private_ips = []
|
|
|
|
for nic in data['nic']:
|
|
if 'ipaddress' not in nic:
|
|
continue
|
|
if is_private_subnet(nic['ipaddress']):
|
|
private_ips.append(nic['ipaddress'])
|
|
else:
|
|
public_ips.append(nic['ipaddress'])
|
|
|
|
security_groups = data.get('securitygroup', [])
|
|
|
|
if security_groups:
|
|
security_groups = [sg['name'] for sg in security_groups]
|
|
|
|
affinity_groups = data.get('affinitygroup', [])
|
|
|
|
if affinity_groups:
|
|
affinity_groups = [ag['id'] for ag in affinity_groups]
|
|
|
|
created = data.get('created', False)
|
|
|
|
extra = self._get_extra_dict(data,
|
|
RESOURCE_EXTRA_ATTRIBUTES_MAP['node'])
|
|
|
|
# Add additional parameters to extra
|
|
extra['security_group'] = security_groups
|
|
extra['affinity_group'] = affinity_groups
|
|
extra['ip_addresses'] = []
|
|
extra['ip_forwarding_rules'] = []
|
|
extra['port_forwarding_rules'] = []
|
|
extra['created'] = created
|
|
|
|
if 'tags' in data:
|
|
extra['tags'] = self._get_resource_tags(data['tags'])
|
|
|
|
node = CloudStackNode(id=id, name=name, state=state,
|
|
public_ips=list(set(public_ips)),
|
|
private_ips=private_ips,
|
|
driver=self, extra=extra)
|
|
return node
|
|
|
|
def _to_key_pairs(self, data):
|
|
key_pairs = [self._to_key_pair(data=item) for item in data]
|
|
return key_pairs
|
|
|
|
def _to_key_pair(self, data):
|
|
key_pair = KeyPair(name=data['name'],
|
|
fingerprint=data['fingerprint'],
|
|
public_key=data.get('publickey', None),
|
|
private_key=data.get('privatekey', None),
|
|
driver=self)
|
|
return key_pair
|
|
|
|
def _to_affinity_group(self, data):
|
|
affinity_group = CloudStackAffinityGroup(
|
|
id=data['id'],
|
|
name=data['name'],
|
|
group_type=CloudStackAffinityGroupType(data['type']),
|
|
account=data.get('account', ''),
|
|
domain=data.get('domain', ''),
|
|
domainid=data.get('domainid', ''),
|
|
description=data.get('description', ''),
|
|
virtualmachine_ids=data.get('virtualmachineIds', ''))
|
|
|
|
return affinity_group
|
|
|
|
def _get_resource_tags(self, tag_set):
|
|
"""
|
|
Parse tags from the provided element and return a dictionary with
|
|
key/value pairs.
|
|
|
|
:param tag_set: A list of key/value tag pairs
|
|
:type tag_set: ``list```
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
tags = {}
|
|
|
|
for tag in tag_set:
|
|
key = tag['key']
|
|
value = tag['value']
|
|
tags[key] = value
|
|
|
|
return tags
|
|
|
|
def _get_extra_dict(self, response, mapping):
|
|
"""
|
|
Extract attributes from the element based on rules provided in the
|
|
mapping dictionary.
|
|
|
|
:param response: The JSON response to parse the values from.
|
|
:type response: ``dict``
|
|
|
|
:param mapping: Dictionary with the extra layout
|
|
:type mapping: ``dict``
|
|
|
|
:rtype: ``dict``
|
|
"""
|
|
extra = {}
|
|
for attribute, values in mapping.items():
|
|
transform_func = values['transform_func']
|
|
value = response.get(values['key_name'], None)
|
|
|
|
if value is not None:
|
|
extra[attribute] = transform_func(value)
|
|
else:
|
|
extra[attribute] = None
|
|
|
|
return extra
|
|
|
|
def _to_volume_state(self, vol):
|
|
state = self.VOLUME_STATE_MAP.get(vol['state'],
|
|
StorageVolumeState.UNKNOWN)
|
|
|
|
# If a volume is 'Ready' and is attached to a virtualmachine, set
|
|
# the status to INUSE
|
|
if state == StorageVolumeState.AVAILABLE and 'virtualmachineid' in vol:
|
|
state = StorageVolumeState.INUSE
|
|
|
|
return state
|