Deploy server uncustomized
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/9d631881 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/9d631881 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/9d631881 Branch: refs/heads/trunk Commit: 9d631881d997a40c62f15e347d8fe9b25cd881f8 Parents: a094ac7 Author: Samuel Chong <[email protected]> Authored: Tue Dec 20 16:35:49 2016 +1100 Committer: Anthony Shaw <[email protected]> Committed: Tue Dec 27 19:22:04 2016 +1100 ---------------------------------------------------------------------- libcloud/compute/drivers/dimensiondata.py | 307 ++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/9d631881/libcloud/compute/drivers/dimensiondata.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/dimensiondata.py b/libcloud/compute/drivers/dimensiondata.py index be471f0..8dbcb14 100644 --- a/libcloud/compute/drivers/dimensiondata.py +++ b/libcloud/compute/drivers/dimensiondata.py @@ -57,6 +57,7 @@ from libcloud.utils.py3 import urlencode, ensure_string from libcloud.utils.xml import fixxpath, findtext, findall from libcloud.utils.py3 import basestring from libcloud.compute.types import NodeState, Provider +import sys # Node state map is a dictionary with the keys as tuples # These tuples represent: @@ -1838,10 +1839,10 @@ class DimensionDataNodeDriver(NodeDriver): response_code = findtext(result, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] - def ex_get_node_by_id(self, id): + def ex_get_node_by_id(self, id, os_customization=True): node = self.connection.request_with_orgId_api_2( 'server/server/%s' % id).object - return self._to_node(node) + return self._to_node(node, os_customization) def ex_list_firewall_rules(self, network_domain, page_size=50, page_number=1): @@ -3804,6 +3805,297 @@ class DimensionDataNodeDriver(NodeDriver): response_code = findtext(response, 'responseCode', TYPES_URN) return response_code in ['IN_PROGRESS', 'OK'] + def ex_create_node_uncustomized(self, + name, + image, + ex_network_domain, + ex_is_started=True, + ex_description=None, + ex_cluster_id=None, + ex_cpu_specification=None, + ex_memory_gb=None, + ex_primary_nic_private_ipv4=None, + ex_primary_nic_vlan=None, + ex_primary_nic_network_adapter=None, + ex_additional_nics=None, + ex_disks=None, + ex_tagid_value_pairs=None, + ex_tagname_value_pairs=None + ): + + """ + This MCP 2.0 only function deploys a new Cloud Server from a + CloudControl compatible Server Image, which does not utilize + VMware Guest OS Customization process. + + Create Node in MCP2 Data Center + + + :keyword name: (required) String with a name for this new node + :type name: ``str`` + + :keyword image: (UUID of the Server Image being used as the target + for the new Server deployment. The source Server + Image (OS Image or Customer Image) must have + osCustomization set to true. See Get/List OS + Image(s) and Get/List Customer Image(s). + :type image: :class:`NodeImage` or ``str`` + + + :keyword ex_network_domain: (required) Network Domain or Network + Domain ID to create the node + :type ex_network_domain: :class:`DimensionDataNetworkDomain` + or ``str`` + + :keyword ex_description: (optional) description for this node + :type ex_description: ``str`` + + :keyword ex_cluster_id: (optional) For multiple cluster + environments, it is possible to set a destination cluster for the new + Customer Image. Note that performance of this function is optimal when + either the Server cluster and destination are the same or when shared + data storage is in place for the multiple clusters. + :type ex_cluster_id: ``str`` + + + :keyword ex_primary_nic_private_ipv4: Provide private IPv4. Ignore + if ex_primary_nic_vlan is + provided. Use one or the + other. Not both. + :type ex_primary_nic_private_ipv4: :``str`` + + :keyword ex_primary_nic_vlan: Provide VLAN for the node if + ex_primary_nic_private_ipv4 NOT + provided. One or the other. Not both. + :type ex_primary_nic_vlan: :class: DimensionDataVlan or ``str`` + + :keyword ex_primary_nic_network_adapter: (Optional) Default value + for the Operating System + will be used if leave + empty. Example: "E1000". + :type ex_primary_nic_network_adapter: :``str`` + + :keyword ex_additional_nics: (optional) List + :class:'DimensionDataNic' or None + :type ex_additional_nics: ``list`` of :class:'DimensionDataNic' + or ``str`` + + :keyword ex_memory_gb: (optional) The amount of memory in GB for + the server Can be used to override the + memory value inherited from the source + Server Image. + :type ex_memory_gb: ``int`` + + :keyword ex_cpu_specification: (optional) The spec of CPU to deploy + :type ex_cpu_specification: + :class:`DimensionDataServerCpuSpecification` + + :keyword ex_is_started: (required) Start server after creation. + Default is set to true. + :type ex_is_started: ``bool`` + + :keyword ex_disks: (optional) Dimensiondata disks. Optional disk + elements can be used to define the disk speed + that each disk on the Server; inherited from the + source Server Image will be deployed to. It is + not necessary to include a diskelement for every + disk; only those that you wish to set a disk + speed value for. Note that scsiId 7 cannot be + used.Up to 13 disks can be present in addition to + the required OS disk on SCSI ID 0. Refer to + https://docs.mcp-services.net/x/UwIu for disk + + :type ex_disks: List or tuple of :class:'DimensionDataServerDisk` + + :keyword ex_tagid_value_pairs: + (Optional) up to 10 tag elements may be provided. + A combination of tagById and tag name cannot be + supplied in the same request. + Note: ex_tagid_value_pairs and + ex_tagname_value_pairs is + mutually exclusive. Use one or other. + + :type ex_tagname_value_pairs: ``list`` of dictionary of tag name + and value pair. Value can be None. + + :keyword ex_tagname_value_pairs: + (Optional) up to 10 tag elements may be provided. + A combination of tagById and tag name cannot be + supplied in the same request. + Note: ex_tagid_value_pairs and + ex_tagname_value_pairs is + mutually exclusive. Use one or other. + + :type ex_tagname_value_pairs: ``list`` of dictionary of tag name + and value pair. Value can be None. + + :return: The newly created :class:`Node`. + :rtype: :class:`Node` + """ + + # Unsupported for version lower than 2.4 + if LooseVersion(self.connection.active_api_version) < LooseVersion( + '2.4'): + raise Exception("This feature is NOT supported in " + "earlier api version of 2.4") + + # Default start to true if input is invalid + if not isinstance(ex_is_started, bool): + ex_is_started = True + print("Warning: ex_is_started input value is invalid. Default" + "to True") + + server_elm = ET.Element('deployUncustomizedServer', + {'xmlns': TYPES_URN}) + ET.SubElement(server_elm, "name").text = name + ET.SubElement(server_elm, "description").text = ex_description + image_id = self._image_to_image_id(image) + ET.SubElement(server_elm, "imageId").text = image_id + + if ex_cluster_id: + dns_elm = ET.SubElement(server_elm, "primaryDns") + dns_elm.text = ex_cluster_id + + if ex_is_started is not None: + ET.SubElement(server_elm, "start").text = str( + ex_is_started).lower() + + if ex_cpu_specification is not None: + cpu = ET.SubElement(server_elm, "cpu") + cpu.set('speed', ex_cpu_specification.performance) + cpu.set('count', str(ex_cpu_specification.cpu_count)) + cpu.set('coresPerSocket', + str(ex_cpu_specification.cores_per_socket)) + + if ex_memory_gb is not None: + ET.SubElement(server_elm, "memoryGb").text = str(ex_memory_gb) + + if (ex_primary_nic_private_ipv4 is None and + ex_primary_nic_vlan is None): + raise ValueError("Missing argument. Either " + "ex_primary_nic_private_ipv4 or " + "ex_primary_nic_vlan " + "must be specified.") + + if (ex_primary_nic_private_ipv4 is not None and + ex_primary_nic_vlan is not None): + raise ValueError("Either ex_primary_nic_private_ipv4 or " + "ex_primary_nic_vlan " + "be specified. Not both.") + + network_elm = ET.SubElement(server_elm, "networkInfo") + + net_domain_id = self._network_domain_to_network_domain_id( + ex_network_domain) + network_elm.set('networkDomainId', net_domain_id) + + pri_nic = ET.SubElement(network_elm, 'primaryNic') + + if ex_primary_nic_private_ipv4 is not None: + ET.SubElement(pri_nic, + 'privateIpv4').text = ex_primary_nic_private_ipv4 + + if ex_primary_nic_vlan is not None: + vlan_id = self._vlan_to_vlan_id(ex_primary_nic_vlan) + ET.SubElement(pri_nic, 'vlanId').text = vlan_id + + if ex_primary_nic_network_adapter is not None: + ET.SubElement(pri_nic, + "networkAdapter").text = \ + ex_primary_nic_network_adapter + + if isinstance(ex_additional_nics, (list, tuple)): + for nic in ex_additional_nics: + additional_nic = ET.SubElement(network_elm, + 'additionalNic') + + if (nic.private_ip_v4 is None and + nic.vlan is None): + raise ValueError("Either a vlan or private_ip_v4 " + "must be specified for each " + "additional nic.") + + if (nic.private_ip_v4 is not None and + nic.vlan is not None): + raise ValueError("Either a vlan or private_ip_v4 " + "must be specified for each " + "additional nic. Not both.") + + if nic.private_ip_v4 is not None: + ET.SubElement(additional_nic, + 'privateIpv4').text = nic.private_ip_v4 + + if nic.vlan is not None: + vlan_id = self._vlan_to_vlan_id(nic.vlan) + ET.SubElement(additional_nic, 'vlanId').text = vlan_id + + if nic.network_adapter_name is not None: + ET.SubElement(additional_nic, + "networkAdapter").text = \ + nic.network_adapter_name + elif ex_additional_nics is not None: + raise TypeError( + "ex_additional_NICs must be None or tuple/list") + + if isinstance(ex_disks, (list, tuple)): + for disk in ex_disks: + disk_elm = ET.SubElement(server_elm, 'disk') + disk_elm.set('scsiId', disk.scsi_id) + disk_elm.set('speed', disk.speed) + elif ex_disks is not None: + raise TypeError("ex_disks must be None or tuple/list") + + # tagid and tagname value pair should not co-exists + if ex_tagid_value_pairs is not None and ex_tagname_value_pairs is \ + not None: + raise ValueError("ex_tagid_value_pairs and ex_tagname_value_pairs" + "is mutually exclusive. Use one or the other.") + + # Tag by ID + if ex_tagid_value_pairs is not None: + if not isinstance(ex_tagid_value_pairs, (list, tuple)): + raise ValueError( + "ex_tagid_value_pairs should be a list of tag ID and " + "value dictionary." + ) + + tag_elem = ET.SubElement(server_elm, 'tagById') + + for k, v in ex_tagid_value_pairs.items(): + ET.SubElement(tag_elem, 'tagKeyId').text = k + + if v is not None: + ET.SubElement(tag_elem, 'value').text = v + + if ex_tagname_value_pairs is not None: + if not isinstance(ex_tagname_value_pairs, (list, tuple)): + raise ValueError( + "ex_tagname_value_pairs should be a list of tag ID and " + "value dictionary." + ) + + tag_elem = ET.SubElement(server_elm, 'tag') + + for k, v in ex_tagid_value_pairs.items(): + ET.SubElement(tag_elem, 'tagKeyName').text = k + + if v is not None: + ET.SubElement(tag_elem, 'value').text = v + + response = self.connection.request_with_orgId_api_2( + 'server/deployUncustomizedServer', + method='POST', + data=ET.tostring(server_elm)).object + + node_id = None + for info in findall(response, 'info', TYPES_URN): + if info.get('name') == 'serverId': + node_id = info.get('value') + + new_node = self.ex_get_node_by_id(node_id, os_customization=False) + + return new_node + def _format_csv(self, http_response): text = http_response.read() lines = str.splitlines(ensure_string(text)) @@ -4158,7 +4450,7 @@ class DimensionDataNodeDriver(NodeDriver): node_elements = object.findall(fixxpath('server', TYPES_URN)) return [self._to_node(el) for el in node_elements] - def _to_node(self, element): + def _to_node(self, element, os_customization=True): started = findtext(element, 'started', TYPES_URN) status = self._to_status(element.find(fixxpath('progress', TYPES_URN))) dd_state = findtext(element, 'state', TYPES_URN) @@ -4170,6 +4462,9 @@ class DimensionDataNodeDriver(NodeDriver): cpu_spec = self._to_cpu_spec(element.find(fixxpath('cpu', TYPES_URN))) disks = self._to_disks(element) + # Vmware Tools + vmware_tools = None + # Version 2.3 or earlier if LooseVersion(self.connection.active_api_version) < LooseVersion( '2.4'): @@ -4177,9 +4472,11 @@ class DimensionDataNodeDriver(NodeDriver): element.find(fixxpath('vmwareTools', TYPES_URN))) operation_system = element.find(fixxpath( 'operatingSystem', TYPES_URN)) + # Version 2.4 or later else: - vmware_tools = self._to_vmware_tools( - element.find(fixxpath('guest/vmTools', TYPES_URN))) + if os_customization: + vmware_tools = self._to_vmware_tools( + element.find(fixxpath('guest/vmTools', TYPES_URN))) operation_system = element.find(fixxpath( 'guest/operatingSystem', TYPES_URN))
