You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2016/12/27 08:26:47 UTC

[05/11] libcloud git commit: Deploy server uncustomized

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 <sa...@gmail.com>
Authored: Tue Dec 20 16:35:49 2016 +1100
Committer: Anthony Shaw <an...@apache.org>
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))