| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| |
| import base64 |
| import warnings |
| |
| from libcloud.utils.py3 import b, urlparse |
| from libcloud.compute.base import ( |
| Node, |
| KeyPair, |
| NodeSize, |
| NodeImage, |
| NodeDriver, |
| NodeLocation, |
| StorageVolume, |
| VolumeSnapshot, |
| ) |
| from libcloud.compute.types import ( |
| NodeState, |
| LibcloudError, |
| StorageVolumeState, |
| KeyPairDoesNotExistError, |
| ) |
| from libcloud.utils.networking import is_private_subnet |
| from libcloud.common.cloudstack import CloudStackDriverMixIn |
| from libcloud.compute.providers import Provider |
| |
| |
| # 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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: |
| """ |
| 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().__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 |