libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From GitBox <...@apache.org>
Subject [GitHub] [libcloud] micafer commented on a change in pull request #1504: Add support for Linode's API v4
Date Mon, 07 Dec 2020 10:38:39 GMT

micafer commented on a change in pull request #1504:
URL: https://github.com/apache/libcloud/pull/1504#discussion_r537401166



##########
File path: libcloud/compute/drivers/linode.py
##########
@@ -728,6 +752,914 @@ def _to_nodes(self, objs):
         return list(nodes.values())
 
 
+class LinodeNodeDriverV4(LinodeNodeDriver):
+
+    connectionCls = LinodeConnectionV4
+    _linode_disk_filesystems = LINODE_DISK_FILESYSTEMS_V4
+
+    LINODE_STATES = {
+        'running': NodeState.RUNNING,
+        'stopped': NodeState.STOPPED,
+        'provisioning': NodeState.STARTING,
+        'offline': NodeState.STOPPED,
+        'booting': NodeState.STARTING,
+        'rebooting': NodeState.REBOOTING,
+        'shutting_down': NodeState.STOPPING,
+        'deleting': NodeState.PENDING,
+        'migrating': NodeState.MIGRATING,
+        'rebuilding': NodeState.UPDATING,
+        'cloning': NodeState.MIGRATING,
+        'restoring': NodeState.PENDING,
+        'resizing': NodeState.RECONFIGURING
+    }
+
+    LINODE_DISK_STATES = {
+        'ready': StorageVolumeState.AVAILABLE,
+        'not ready': StorageVolumeState.CREATING,
+        'deleting': StorageVolumeState.DELETING
+    }
+
+    LINODE_VOLUME_STATES = {
+        'creating': StorageVolumeState.CREATING,
+        'active': StorageVolumeState.AVAILABLE,
+        'resizing': StorageVolumeState.UPDATING,
+        'contact_support': StorageVolumeState.UNKNOWN
+    }
+
+    def list_nodes(self):
+        """
+        Returns a list of Linodes the API key in use has access
+        to view.
+
+        :return: List of node objects
+        :rtype: ``list`` of :class:`Node`
+        """
+
+        data = self._paginated_request('/v4/linode/instances', 'data')
+        return [self._to_node(obj) for obj in data]
+
+    def list_sizes(self):
+        """
+        Returns a list of Linode Types
+
+        : rtype: ``list`` of :class: `NodeSize`
+        """
+        data = self._paginated_request('/v4/linode/types', 'data')
+        return [self._to_size(obj) for obj in data]
+
+    def list_images(self):
+        """
+        Returns a list of images
+
+        :rtype: ``list`` of :class:`NodeImage`
+        """
+        data = self._paginated_request('/v4/images', 'data')
+        return [self._to_image(obj) for obj in data]
+
+    def list_locations(self):
+        """
+        Lists the Regions available for Linode services
+
+        :rtype: ``list`` of :class:`NodeLocation`
+        """
+        data = self._paginated_request('/v4/regions', 'data')
+        return [self._to_location(obj) for obj in data]
+
+    def start_node(self, node):
+        """Boots a node the API Key has permission to modify
+
+        :param       node: the node to start
+        :type        node: :class:`Node`
+
+        :rtype: ``bool``
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        response = self.connection.request('/v4/linode/instances/%s/boot'
+                                           % node.id,
+                                           method='POST')
+        return response.status == httplib.OK
+
+    def ex_start_node(self, node):
+        # NOTE: This method is here for backward compatibility reasons after
+        # this method was promoted to be part of the standard compute API in
+        # Libcloud v2.7.0
+        return self.start_node(node=node)
+
+    def stop_node(self, node):
+        """Shuts down a a node the API Key has permission to modify.
+
+        :param       node: the Linode to destroy
+        :type        node: :class:`Node`
+
+        :rtype: ``bool``
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        response = self.connection.request('/v4/linode/instances/%s/shutdown'
+                                           % node.id,
+                                           method='POST')
+        return response.status == httplib.OK
+
+    def ex_stop_node(self, node):
+        # NOTE: This method is here for backward compatibility reasons after
+        # this method was promoted to be part of the standard compute API in
+        # Libcloud v2.7.0
+        return self.stop_node(node=node)
+
+    def destroy_node(self, node):
+        """Deletes a node the API Key has permission to `read_write`
+
+        :param       node: the Linode to destroy
+        :type        node: :class:`Node`
+
+        :rtype: ``bool``
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        response = self.connection.request('/v4/linode/instances/%s'
+                                           % node.id,
+                                           method='DELETE')
+        return response.status == httplib.OK
+
+    def reboot_node(self, node):
+        """Reboots a node the API Key has permission to modify.
+
+        :param       node: the Linode to destroy
+        :type        node: :class:`Node`
+
+        :rtype: ``bool``
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        response = self.connection.request('/v4/linode/instances/%s/reboot'
+                                           % node.id,
+                                           method='POST')
+        return response.status == httplib.OK
+
+    def create_node(self, location, size, image=None,
+                    name=None, root_pass=None, ex_authorized_keys=None,
+                    ex_authorized_users=None, ex_tags=None,
+                    ex_backups_enabled=False, ex_private_ip=False):
+        """Creates a Linode Instance.
+        In order for this request to complete successfully,
+        the user must have the `add_linodes` grant as this call
+        will incur a charge.
+
+        :param location: which region to create the node in
+        :type    location: :class:`NodeLocation`
+
+        :param size: the plan size to create
+        :type    size: :class:`NodeSize`
+
+        :keyword image: which distribution to deploy on the node
+        :type    image: :class:`NodeImage`
+
+        :keyword name: the name to assign to node.\
+        Must start with an alpha character.\
+        May only consist of alphanumeric characters,\
+         dashes (-), underscores (_) or periods (.).\
+        Cannot have two dashes (--), underscores (__) or periods (..) in a row.
+        :type    name: ``str``
+
+        :keyword root_pass: the root password (required if image is provided)
+        :type    root_pass: ``str``
+
+        :keyword ex_authorized_keys: a list of public SSH keys
+        :type    ex_authorized_keys: ``list`` of ``str``
+
+        :keyword ex_authorized_users:  a list of usernames.\
+        If the usernames have associated SSH keys,\
+        the keys will be appended to the root users `authorized_keys`
+        :type    ex_authorized_users: ``list`` of ``str``
+
+        :keyword ex_tags: list of tags for the node
+        :type    ex_tags: ``list`` of ``str``
+
+        :keyword ex_backups_enabled: whether to be enrolled \
+        in the Linode Backup service (False)
+        :type    ex_backups_enabled: ``bool``
+
+        :keyword ex_private_ip: whether or not to request a private IP
+        :type    ex_private_ip: ``bool``
+
+        :return: Node representing the newly-created node
+        :rtype: :class:`Node`
+        """
+
+        if not isinstance(location, NodeLocation):
+            raise LinodeExceptionV4("Invalid location instance")
+
+        if not isinstance(size, NodeSize):
+            raise LinodeExceptionV4("Invalid size instance")
+
+        attr = {'region': location.id,
+                'type': size.id,
+                'private_ip': ex_private_ip,
+                'backups_enabled': ex_backups_enabled,
+                }
+
+        if image is not None:
+            if root_pass is None:
+                raise LinodeExceptionV4("root password required "
+                                        "when providing an image")
+            attr['image'] = image.id
+            attr['root_pass'] = root_pass
+
+        if name is not None:
+            valid_name = r'^[a-zA-Z]((?!--|__|\.\.)[a-zA-Z0-9-_.])+$'
+            if not re.match(valid_name, name):
+                raise LinodeExceptionV4("Invalid name")
+            attr['label'] = name
+        if ex_authorized_keys is not None:
+            attr['authorized_keys'] = list(ex_authorized_keys)
+        if ex_authorized_users is not None:
+            attr['authorized_users'] = list(ex_authorized_users)
+        if ex_tags is not None:
+            attr['tags'] = list(ex_tags)
+
+        response = self.connection.request('/v4/linode/instances',
+                                           data=json.dumps(attr),
+                                           method='POST').object
+        return self._to_node(response)
+
+    def ex_list_disks(self, node):
+        """
+        List disks associated with the node.
+
+        :param    node: Node to list disks. (required)
+        :type       node: :class:`Node`
+
+        :rtype: ``list`` of :class:`LinodeDisk`
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        data = self._paginated_request('/v4/linode/instances/%s/disks'
+                                       % node.id, 'data')
+
+        return [self._to_disk(obj) for obj in data]
+
+    def ex_create_disk(self, size, name, node, fs_type,
+                       image=None, ex_root_pass=None, ex_authorized_keys=None,
+                       ex_authorized_users=None, ex_read_only=False):
+        """
+        Adds a new disk to node
+
+        :param    size: Size of disk in megabytes (required)
+        :type       size: ``int``
+
+        :param    name: Name of the disk to be created (required)
+        :type       name: ``str``
+
+        :param    node: Node to attach disk to (required)
+        :type       node: :class:`Node`
+
+        :param    fs_type: The formatted type of this disk. Valid types are:
+                             ext3, ext4, swap, raw, initrd
+        :type       fs_type: ``str``
+
+        :keyword    image: Image  to deploy the volume from
+        :type       image: :class:`NodeImage`
+
+        :keyword    ex_root_pass: root password,required \
+                    if an image is provided
+        :type       ex_root_pass: ``str``
+
+        :keyword ex_authorized_keys:  a list of SSH keys
+        :type    ex_authorized_keys: ``list`` of ``str``
+
+        :keyword ex_authorized_users:  a list of usernames \
+                 that will have their SSH keys,\
+                 if any, automatically appended \
+                 to the root user's ~/.ssh/authorized_keys file.
+        :type    ex_authorized_users: ``list`` of ``str``
+
+        :keyword ex_read_only: if true, this disk is read-only
+        :type ex_read_only: ``bool``
+
+        :return: LinodeDisk representing the newly-created disk
+        :rtype: :class:`LinodeDisk`
+        """
+
+        attr = {'label': str(name),
+                'size': int(size),
+                'filesystem': fs_type,
+                'read_only': ex_read_only}
+
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        if fs_type not in self._linode_disk_filesystems:
+            raise LinodeExceptionV4("Not valid filesystem type")
+
+        if image is not None:
+            if not isinstance(image, NodeImage):
+                raise LinodeExceptionV4("Invalid image instance")
+            # when an image is set, root pass must be set as well
+            if ex_root_pass is None:
+                raise LinodeExceptionV4("root_pass is required when "
+                                        "deploying an image")
+            attr['image'] = image.id
+            attr['root_pass'] = ex_root_pass
+
+        if ex_authorized_keys is not None:
+            attr['authorized_keys'] = list(ex_authorized_keys)
+
+        if ex_authorized_users is not None:
+            attr['authorized_users'] = list(ex_authorized_users)
+
+        response = self.connection.request('/v4/linode/instances/%s/disks'
+                                           % node.id,
+                                           data=json.dumps(attr),
+                                           method='POST').object
+        return self._to_disk(response)
+
+    def ex_destroy_disk(self, node, disk):
+        """
+        Destroys disk for the given node.
+
+        :param node: The Node the disk is attached to. (required)
+        :type    node: :class:`Node`
+
+        :param disk: LinodeDisk to be destroyed (required)
+        :type disk: :class:`LinodeDisk`
+
+        :rtype: ``bool``
+        """
+        if not isinstance(node, Node):
+            raise LinodeExceptionV4("Invalid node instance")
+
+        if not isinstance(disk, LinodeDisk):
+            raise LinodeExceptionV4("Invalid disk instance")
+
+        if node.state != self.LINODE_STATES['stopped']:
+            raise LinodeExceptionV4("Node needs to be stopped"
+                                    " before disk is destroyed")
+
+        response = self.connection.request('/v4/linode/instances/%s/disks/%s'
+                                           % (node.id, disk.id),
+                                           method='DELETE')
+        return response.status == httplib.OK
+
+    def list_volumes(self):
+        """Get all volumes of the account
+        :rtype: `list` of :class: `StorageVolume`
+        """
+        data = self._paginated_request('/v4/volumes', 'data')
+
+        return [self._to_volume(obj) for obj in data]
+
+    def create_volume(self, name, size, location=None, node=None, tags=None):
+        """Creates a volume and optionally attaches it to a node.
+
+        :param name: The name to be given to volume (required).\
+        Must start with an alpha character. \
+        May only consist of alphanumeric characters,\
+         dashes (-), underscores (_)\
+        Cannot have two dashes (--), underscores (__) in a row.
+
+        :type name: `str`
+
+        :param size: Size in gigabytes (required)
+        :type size: `int`
+
+        :keyword location: Location to create the node.\
+        Required if node is not given.
+        :type location: :class:`NodeLocation`
+
+        :keyword volume: Node to attach the volume to
+        :type volume: :class:`Node`
+
+        :keyword tags: tags to apply to volume
+        :type tags: `list` of `str`
+
+        :rtype: :class: `StorageVolume`
+        """
+
+        valid_name = '^[a-zA-Z]((?!--|__)[a-zA-Z0-9-_])+$'
+        if not re.match(valid_name, name):
+            raise LinodeExceptionV4("Invalid name")
+
+        attr = {
+            'label': name,
+            'size': int(size),
+        }
+
+        if node is not None:
+            if not isinstance(node, Node):
+                raise LinodeExceptionV4("Invalid node instance")
+            attr['linode_id'] = int(node.id)
+        else:
+            # location is only required if a node is not given
+            if location:
+                if not isinstance(location, NodeLocation):
+                    raise LinodeExceptionV4("Invalid location instance")
+                attr['region'] = location.id
+            else:
+                raise LinodeExceptionV4("Region must be provided "
+                                        "when node is not")
+        if tags is not None:
+            attr['tags'] = list(tags)
+
+        response = self.connection.request('/v4/volumes',
+                                           data=json.dumps(attr),
+                                           method='POST').object
+        return self._to_volume(response)
+
+    def attach_volume(self, node, volume, persist_across_boots=True):
+        """Attaches a volume to a node.
+        Volume and node must be located in the same region
+
+        :param volume: Volume to be attached (required)
+        :type volume: :class:`StorageVolume`
+
+        :param volume: Node to attach the volume to(required)
+        :type volume: :class:`Node`
+
+        :keyword persist_across_boots: Node to attach the volume to(required)
+        :type volume: :class:`Node`

Review comment:
       It should be:
   :type persist_across_boots: `bool`




----------------------------------------------------------------
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.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



Mime
View raw message