Kami commented on a change in pull request #1610:
URL: https://github.com/apache/libcloud/pull/1610#discussion_r736419473



##########
File path: libcloud/compute/drivers/vultr.py
##########
@@ -1014,3 +1037,970 @@ def _to_image(self, data):
     def _to_ssh_key(self, data):
         return SSHKey(id=data['SSHKEYID'], name=data['name'],
                       pub_key=data['ssh_key'])
+
+
+class VultrNodeDriverV2(VultrNodeDriver):
+    """
+    Vultr API v2 NodeDriver.
+    """
+    connectionCls = VultrConnectionV2
+    NODE_STATE_MAP = {
+        'active': NodeState.RUNNING,
+        'halted': NodeState.STOPPED,
+        'rebooting': NodeState.REBOOTING,
+        'resizing': NodeState.RECONFIGURING,
+        'pending': NodeState.PENDING,
+    }
+
+    VOLUME_STATE_MAP = {
+        'active': StorageVolumeState.AVAILABLE,
+        'pending': StorageVolumeState.CREATING,
+    }
+
+    SNAPSHOT_STATE_MAP = {
+        'complete': VolumeSnapshotState.AVAILABLE,
+        'pending': VolumeSnapshotState.CREATING,
+    }
+
+    def list_nodes(self, ex_list_bare_metals=True):
+        """List all nodes.
+
+        :keyword ex_list_bare_metals: Whether to fetch bare metal nodes.
+        :type    ex_list_bare_metals: ``bool``
+
+        :return:  list of node objects
+        :rtype: ``list`` of :class: `Node`
+        """
+        data = self._paginated_request('/v2/instances', 'instances')
+        nodes = [self._to_node(item) for item in data]
+
+        if ex_list_bare_metals is True:
+            nodes += self.ex_list_bare_metal_nodes()
+        return nodes
+
+    def create_node(self, name, size, location, image=None,
+                    ex_ssh_key_ids=None, ex_private_network_ids=None,
+                    ex_snapshot=None, ex_enable_ipv6=False, ex_backups=False,
+                    ex_userdata=None, ex_ddos_protection=False,
+                    ex_enable_private_network=False, ex_ipxe_chain_url=None,
+                    ex_iso_id=None, ex_script_id=None, ex_image_id=None,
+                    ex_activation_email=False, ex_hostname=None, ex_tag=None,
+                    ex_firewall_group_id=None, ex_reserved_ipv4=None,
+                    ex_persistent_pxe=False):
+        """Create a new node.
+
+        :param name: The new node's name.
+        :type name: ``str``
+
+        :param size: The size to use to create the node.
+        :type size: :class: `NodeSize`
+
+        :param location: The location to provision the node.
+        :type location: :class: `NodeLocation`
+
+        :keyword image:  The image to use to provision the node.
+        :type    image:  :class: `NodeImage`
+
+        :keyword ex_ssh_key_ids: List of SSH keys to install on this node.
+        :type    ex_ssh_key_ids: ``list`` of ``str``
+
+        :keyword ex_private_network_ids: The network ids to attach to node.
+                                         This parameter takes precedence over
+                                         ex_enable_private_network (VPS only)
+        :type    ex_private_network_ids: ``list`` of ``str``
+
+        :keyword ex_snapshot: The snapshot to use when deploying the node.
+                                 Mutually exclusive with image,
+        :type    ex_snapshot: :class: `VultrNodeSnapshot` or ``str``
+
+        :keyword ex_enable_ipv6: Wheteher to enable IPv6.
+        :type    ex_enable_ipv6: ``bool``
+
+        :keyword ex_backups: Enable automatic backups for the node. (VPS only)
+        :type    ex_backups: ``bool``
+
+        :keyword ex_userdata: String containing user data
+        :type    ex_userdata: ``str``
+
+        :keyword ex_ddos_protection: Enable DDoS protection (VPS only)
+        :type    ex_ddos_protection: ``bool``
+
+        :keyword ex_enable_private_network: Enable private networking.
+                                            Mutually exclusive with
+                                             ex_private_network_ids.
+                                            (VPS only)
+        :type    ex_enable_private_network: ``bool``
+
+        :keyword ex_ipxe_chain_url: The URL location of the iPXE chainloader
+                                    (VPS only)
+        :type    ex_ipxe_chain_url: ``str``
+
+        :keyword ex_iso_id: The ISO id to use when deploying this node.
+                            (VPS only)
+        :type    ex_iso_id: ``str``
+
+        :keyword ex_script_id: The startup script id to use when deploying
+                               this node.
+        :type    ex_script_id: ``str``
+
+        :keyword ex_image_id: The Application image_id to use when deploying
+                              this node.
+        :type    ex_image_id: ``str``
+
+        :keyword ex_activation_email: Notify by email after deployment.
+        :type    ex_activation_email: ``bool``
+
+        :keyword ex_hostname: The hostname to use when deploying this node.
+        :type    ex_hostname: ``str``
+
+        :keyword ex_tag: The user-supplied tag.
+        :type    ex_tag: ``str``
+
+        :keyword ex_firewall_group_id: The Firewall Group id to attach to
+                                       this node. (VPS only)
+        :type    ex_firewall_group_id: ``str``
+
+        :keyword ex_reserved_ipv4: Id of the floating IP to use as the
+                                   main IP of this node.
+        :type    ex_reserved_ipv4: ``str``
+
+        :keyword ex_persistent_pxe: Enable persistent PXE (Bare Metal only)
+        :type    ex_persistent_pxe: ``bool``
+        """
+        data = {
+            'label': name,
+            'region': location.id,
+            'plan': size.id,
+            'enable_ipv6': ex_enable_ipv6,
+            'activation_email': ex_activation_email,
+        }
+
+        if image:
+            data['os_id'] = image.id
+
+        if ex_ssh_key_ids:
+            data['sshkey_id'] = ex_ssh_key_ids
+
+        if ex_snapshot:
+            try:
+                data['snapshot_id'] = ex_snapshot.id
+            except AttributeError:
+                data['snapshot_id'] = ex_snapshot
+
+        if ex_userdata:
+            data['user_data'] = base64.b64encode(
+                bytes(ex_userdata, 'utf-8')).decode('utf-8')
+
+        if ex_script_id:
+            data['script_id'] = ex_script_id
+
+        if ex_image_id:
+            data['image_id'] = ex_image_id
+
+        if ex_hostname:
+            data['hostname'] = ex_hostname
+
+        if ex_reserved_ipv4:
+            data['reserved_ipv4'] = ex_reserved_ipv4
+
+        if ex_tag:
+            data['tag'] = ex_tag
+
+        if self._is_bare_metal(size):
+            if ex_persistent_pxe:
+                data['persistent_pxe'] = ex_persistent_pxe
+            resp = self.connection.request('/v2/bare-metals',
+                                           data=json.dumps(data),
+                                           method='POST')
+            return self._to_node(resp.object['bare_metal'])
+        else:
+            if ex_private_network_ids:
+                data['attach_private_network'] = ex_private_network_ids
+
+            if ex_enable_private_network:
+                data['enable_private_network'] = ex_enable_private_network
+
+            if ex_ipxe_chain_url:
+                data['ipxe_chain_url'] = ex_ipxe_chain_url
+
+            if ex_iso_id:
+                data['iso_id'] = ex_iso_id
+
+            if ex_ddos_protection:
+                data['ddos_protection'] = ex_ddos_protection
+
+            if ex_firewall_group_id:
+                data['firewall_group_id'] = ex_firewall_group_id
+
+            if ex_backups:
+                data['backups'] = ('enabled' if ex_backups is True
+                                   else 'disabled')
+
+            resp = self.connection.request('/v2/instances',
+                                           data=json.dumps(data),
+                                           method='POST')
+            return self._to_node(resp.object['instance'])
+
+    def reboot_node(self, node):
+        """Reboot the given node.
+
+        :param node: The node to be rebooted.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_reboot_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s/reboot' % node.id,
+                                       method='POST')
+
+        return resp.success()
+
+    def start_node(self, node):
+        """Start the given node.
+
+        :param node: The node to be started.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_start_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s/start' % node.id,
+                                       method='POST')
+
+        return resp.success()
+
+    def stop_node(self, node):
+        """Stop the given node.
+
+        :param node: The node to be stopped.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_stop_bare_metal_node(node)
+
+        return self.ex_stop_nodes([node])
+
+    def destroy_node(self, node):
+        """Destroy the given node.
+
+        :param node: The node to be destroyed.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_destroy_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s' % node.id,
+                                       method='DELETE')
+
+        return resp.success()
+
+    def list_sizes(self, ex_list_bare_metals=True):
+        """List available node sizes.
+
+        :keyword ex_list_bare_metals: Whether to fetch bare metal sizes.
+        :type    ex_list_bare_metals: ``bool``
+
+        :rtype: ``list`` of :class: `NodeSize`
+        """
+        data = self._paginated_request('/v2/plans', 'plans')
+        sizes = [self._to_size(item) for item in data]
+
+        if ex_list_bare_metals is True:
+            sizes += self.ex_list_bare_metal_sizes()
+        return sizes
+
+    def list_images(self):
+        """List available node images.
+
+        :rtype: ``list`` of :class: `NodeImage`
+        """
+        data = self._paginated_request('/v2/os', 'os')
+        return [self._to_image(item) for item in data]
+
+    def list_locations(self):
+        """List available node locations.
+
+        :rtype: ``list`` of :class: `NodeLocation`
+        """
+        data = self._paginated_request('/v2/regions', 'regions')
+        return [self._to_location(item) for item in data]
+
+    def list_volumes(self):
+        """List storage volumes.
+
+        :rtype: ``list`` of :class:`StorageVolume`
+        """
+        data = self._paginated_request('/v2/blocks', 'blocks')
+        return [self._to_volume(item) for item in data]
+
+    def create_volume(self, size, name, location):
+        """Create a new volume.
+
+        :param size: Size of the volume in gigabytes.\
+        Size may range between 10 and 10000.
+        :type size: ``int``
+
+        :param name: Name of the volume to be created.
+        :type name: ``str``
+
+        :param location: Which data center to create the volume in.
+        :type location: :class:`NodeLocation` or ``str``
+
+        :return: The newly created volume.
+        :rtype: :class:`StorageVolume`
+        """
+
+        data = {
+            'label': name,
+            'size_gb': size,
+        }
+        try:
+            data['region'] = location.id
+        except AttributeError:
+            data['region'] = location
+
+        resp = self.connection.request('/v2/blocks',
+                                       data=json.dumps(data),
+                                       method='POST')
+        return self._to_volume(resp.object['block'])
+
+    def attach_volume(self, node, volume, live=True):

Review comment:
       ``live`` argument is not part of the standard API so it should have 
``ex_`` prefix. 

##########
File path: libcloud/compute/drivers/vultr.py
##########
@@ -1014,3 +1037,970 @@ def _to_image(self, data):
     def _to_ssh_key(self, data):
         return SSHKey(id=data['SSHKEYID'], name=data['name'],
                       pub_key=data['ssh_key'])
+
+
+class VultrNodeDriverV2(VultrNodeDriver):
+    """
+    Vultr API v2 NodeDriver.
+    """
+    connectionCls = VultrConnectionV2
+    NODE_STATE_MAP = {
+        'active': NodeState.RUNNING,
+        'halted': NodeState.STOPPED,
+        'rebooting': NodeState.REBOOTING,
+        'resizing': NodeState.RECONFIGURING,
+        'pending': NodeState.PENDING,
+    }
+
+    VOLUME_STATE_MAP = {
+        'active': StorageVolumeState.AVAILABLE,
+        'pending': StorageVolumeState.CREATING,
+    }
+
+    SNAPSHOT_STATE_MAP = {
+        'complete': VolumeSnapshotState.AVAILABLE,
+        'pending': VolumeSnapshotState.CREATING,
+    }
+
+    def list_nodes(self, ex_list_bare_metals=True):
+        """List all nodes.
+
+        :keyword ex_list_bare_metals: Whether to fetch bare metal nodes.
+        :type    ex_list_bare_metals: ``bool``
+
+        :return:  list of node objects
+        :rtype: ``list`` of :class: `Node`
+        """
+        data = self._paginated_request('/v2/instances', 'instances')
+        nodes = [self._to_node(item) for item in data]
+
+        if ex_list_bare_metals is True:
+            nodes += self.ex_list_bare_metal_nodes()
+        return nodes
+
+    def create_node(self, name, size, location, image=None,
+                    ex_ssh_key_ids=None, ex_private_network_ids=None,
+                    ex_snapshot=None, ex_enable_ipv6=False, ex_backups=False,
+                    ex_userdata=None, ex_ddos_protection=False,
+                    ex_enable_private_network=False, ex_ipxe_chain_url=None,
+                    ex_iso_id=None, ex_script_id=None, ex_image_id=None,
+                    ex_activation_email=False, ex_hostname=None, ex_tag=None,
+                    ex_firewall_group_id=None, ex_reserved_ipv4=None,
+                    ex_persistent_pxe=False):
+        """Create a new node.
+
+        :param name: The new node's name.
+        :type name: ``str``
+
+        :param size: The size to use to create the node.
+        :type size: :class: `NodeSize`
+
+        :param location: The location to provision the node.
+        :type location: :class: `NodeLocation`
+
+        :keyword image:  The image to use to provision the node.
+        :type    image:  :class: `NodeImage`
+
+        :keyword ex_ssh_key_ids: List of SSH keys to install on this node.
+        :type    ex_ssh_key_ids: ``list`` of ``str``
+
+        :keyword ex_private_network_ids: The network ids to attach to node.
+                                         This parameter takes precedence over
+                                         ex_enable_private_network (VPS only)
+        :type    ex_private_network_ids: ``list`` of ``str``
+
+        :keyword ex_snapshot: The snapshot to use when deploying the node.
+                                 Mutually exclusive with image,
+        :type    ex_snapshot: :class: `VultrNodeSnapshot` or ``str``
+
+        :keyword ex_enable_ipv6: Wheteher to enable IPv6.
+        :type    ex_enable_ipv6: ``bool``
+
+        :keyword ex_backups: Enable automatic backups for the node. (VPS only)
+        :type    ex_backups: ``bool``
+
+        :keyword ex_userdata: String containing user data
+        :type    ex_userdata: ``str``
+
+        :keyword ex_ddos_protection: Enable DDoS protection (VPS only)
+        :type    ex_ddos_protection: ``bool``
+
+        :keyword ex_enable_private_network: Enable private networking.
+                                            Mutually exclusive with
+                                             ex_private_network_ids.
+                                            (VPS only)
+        :type    ex_enable_private_network: ``bool``
+
+        :keyword ex_ipxe_chain_url: The URL location of the iPXE chainloader
+                                    (VPS only)
+        :type    ex_ipxe_chain_url: ``str``
+
+        :keyword ex_iso_id: The ISO id to use when deploying this node.
+                            (VPS only)
+        :type    ex_iso_id: ``str``
+
+        :keyword ex_script_id: The startup script id to use when deploying
+                               this node.
+        :type    ex_script_id: ``str``
+
+        :keyword ex_image_id: The Application image_id to use when deploying
+                              this node.
+        :type    ex_image_id: ``str``
+
+        :keyword ex_activation_email: Notify by email after deployment.
+        :type    ex_activation_email: ``bool``
+
+        :keyword ex_hostname: The hostname to use when deploying this node.
+        :type    ex_hostname: ``str``
+
+        :keyword ex_tag: The user-supplied tag.
+        :type    ex_tag: ``str``
+
+        :keyword ex_firewall_group_id: The Firewall Group id to attach to
+                                       this node. (VPS only)
+        :type    ex_firewall_group_id: ``str``
+
+        :keyword ex_reserved_ipv4: Id of the floating IP to use as the
+                                   main IP of this node.
+        :type    ex_reserved_ipv4: ``str``
+
+        :keyword ex_persistent_pxe: Enable persistent PXE (Bare Metal only)
+        :type    ex_persistent_pxe: ``bool``
+        """
+        data = {
+            'label': name,
+            'region': location.id,
+            'plan': size.id,
+            'enable_ipv6': ex_enable_ipv6,
+            'activation_email': ex_activation_email,
+        }
+
+        if image:
+            data['os_id'] = image.id
+
+        if ex_ssh_key_ids:
+            data['sshkey_id'] = ex_ssh_key_ids
+
+        if ex_snapshot:
+            try:
+                data['snapshot_id'] = ex_snapshot.id
+            except AttributeError:
+                data['snapshot_id'] = ex_snapshot
+
+        if ex_userdata:
+            data['user_data'] = base64.b64encode(
+                bytes(ex_userdata, 'utf-8')).decode('utf-8')
+
+        if ex_script_id:
+            data['script_id'] = ex_script_id
+
+        if ex_image_id:
+            data['image_id'] = ex_image_id
+
+        if ex_hostname:
+            data['hostname'] = ex_hostname
+
+        if ex_reserved_ipv4:
+            data['reserved_ipv4'] = ex_reserved_ipv4
+
+        if ex_tag:
+            data['tag'] = ex_tag
+
+        if self._is_bare_metal(size):
+            if ex_persistent_pxe:
+                data['persistent_pxe'] = ex_persistent_pxe
+            resp = self.connection.request('/v2/bare-metals',
+                                           data=json.dumps(data),
+                                           method='POST')
+            return self._to_node(resp.object['bare_metal'])
+        else:
+            if ex_private_network_ids:
+                data['attach_private_network'] = ex_private_network_ids
+
+            if ex_enable_private_network:
+                data['enable_private_network'] = ex_enable_private_network
+
+            if ex_ipxe_chain_url:
+                data['ipxe_chain_url'] = ex_ipxe_chain_url
+
+            if ex_iso_id:
+                data['iso_id'] = ex_iso_id
+
+            if ex_ddos_protection:
+                data['ddos_protection'] = ex_ddos_protection
+
+            if ex_firewall_group_id:
+                data['firewall_group_id'] = ex_firewall_group_id
+
+            if ex_backups:
+                data['backups'] = ('enabled' if ex_backups is True
+                                   else 'disabled')
+
+            resp = self.connection.request('/v2/instances',
+                                           data=json.dumps(data),
+                                           method='POST')
+            return self._to_node(resp.object['instance'])
+
+    def reboot_node(self, node):
+        """Reboot the given node.
+
+        :param node: The node to be rebooted.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_reboot_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s/reboot' % node.id,
+                                       method='POST')
+
+        return resp.success()
+
+    def start_node(self, node):
+        """Start the given node.
+
+        :param node: The node to be started.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_start_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s/start' % node.id,
+                                       method='POST')
+
+        return resp.success()
+
+    def stop_node(self, node):
+        """Stop the given node.
+
+        :param node: The node to be stopped.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_stop_bare_metal_node(node)
+
+        return self.ex_stop_nodes([node])
+
+    def destroy_node(self, node):
+        """Destroy the given node.
+
+        :param node: The node to be destroyed.
+        :type node: :class: `Node`
+
+        :rtype: ``bool``
+        """
+        if self._is_bare_metal(node.size) is True:
+            return self.ex_destroy_bare_metal_node(node)
+
+        resp = self.connection.request('/v2/instances/%s' % node.id,
+                                       method='DELETE')
+
+        return resp.success()
+
+    def list_sizes(self, ex_list_bare_metals=True):
+        """List available node sizes.
+
+        :keyword ex_list_bare_metals: Whether to fetch bare metal sizes.
+        :type    ex_list_bare_metals: ``bool``
+
+        :rtype: ``list`` of :class: `NodeSize`
+        """
+        data = self._paginated_request('/v2/plans', 'plans')
+        sizes = [self._to_size(item) for item in data]
+
+        if ex_list_bare_metals is True:
+            sizes += self.ex_list_bare_metal_sizes()
+        return sizes
+
+    def list_images(self):
+        """List available node images.
+
+        :rtype: ``list`` of :class: `NodeImage`
+        """
+        data = self._paginated_request('/v2/os', 'os')
+        return [self._to_image(item) for item in data]
+
+    def list_locations(self):
+        """List available node locations.
+
+        :rtype: ``list`` of :class: `NodeLocation`
+        """
+        data = self._paginated_request('/v2/regions', 'regions')
+        return [self._to_location(item) for item in data]
+
+    def list_volumes(self):
+        """List storage volumes.
+
+        :rtype: ``list`` of :class:`StorageVolume`
+        """
+        data = self._paginated_request('/v2/blocks', 'blocks')
+        return [self._to_volume(item) for item in data]
+
+    def create_volume(self, size, name, location):
+        """Create a new volume.
+
+        :param size: Size of the volume in gigabytes.\
+        Size may range between 10 and 10000.
+        :type size: ``int``
+
+        :param name: Name of the volume to be created.
+        :type name: ``str``
+
+        :param location: Which data center to create the volume in.
+        :type location: :class:`NodeLocation` or ``str``
+
+        :return: The newly created volume.
+        :rtype: :class:`StorageVolume`
+        """
+
+        data = {
+            'label': name,
+            'size_gb': size,
+        }
+        try:
+            data['region'] = location.id
+        except AttributeError:
+            data['region'] = location
+
+        resp = self.connection.request('/v2/blocks',
+                                       data=json.dumps(data),
+                                       method='POST')
+        return self._to_volume(resp.object['block'])
+
+    def attach_volume(self, node, volume, live=True):
+        """Attaches volume to node.
+
+        :param node: Node to attach volume to.
+        :type node: :class:`Node`
+
+        :param volume: Volume to attach.
+        :type volume: :class:`StorageVolume`
+
+        :param live: Attach the volume without restarting the node.
+        :type live: ``bool``
+
+        :rytpe: ``bool``
+        """
+
+        data = {
+            'instance_id': node.id,
+            'live': live,
+        }
+        resp = self.connection.request('/v2/blocks/%s/attach' % volume.id,
+                                       data=json.dumps(data),
+                                       method='POST')
+
+        return resp.success()
+
+    def detach_volume(self, volume, live=True):

Review comment:
       Same here.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to