Source code for doapi.droplet

from six.moves import map  # pylint: disable=redefined-builtin
from .base     import Actionable, ResourceWithID, Region, Size, Kernel, \
                        Networks, BackupWindow, Taggable, fromISO8601
from .image    import Image

[docs]class Droplet(Actionable, ResourceWithID, Taggable): """ A droplet resource, representing a virtual machine provided by DigitalOcean. New droplets are created via the :meth:`doapi.create_droplet` and :meth:`doapi.create_multiple_droplets` methods and can be retrieved with the :meth:`doapi.fetch_droplet` and :meth:`doapi.fetch_all_droplets` methods. The DigitalOcean API specifies the following fields for droplet objects: :var id: a unique identifier for the droplet :vartype id: int :var backup_ids: image IDs of backups taken of the droplet :vartype backup_ids: list of integers :var created_at: date & time of the droplet's creation :vartype created_at: datetime.datetime :var disk: size of the droplet's disk in gigabytes :vartype disk: number :var features: a list of strings naming the features enabled on the droplet :vartype features: list of strings :var image: the base image used to create the droplet :vartype image: `Image` :var kernel: the droplet's current kernel :vartype kernel: `Kernel` or `None` :var locked: whether the droplet is currently locked, preventing actions on it :vartype locked: bool :var memory: RAM of the droplet in megabytes :vartype memory: number :var name: a human-readable name for the droplet :vartype name: string :var networks: the network interfaces configured for the droplet :vartype networks: `Networks` :var next_backup_window: the start & end of the next timeframe in which the droplet will be backed up; only defined if backups are enabled on the droplet :vartype next_backup_window: `BackupWindow` or `None` :var region: the region in which the droplet is located :vartype region: `Region` :var size: the current size of the droplet :vartype size: `Size` :var size_slug: the unique slug identifier for the droplet's size :vartype size_slug: string :var snapshot_ids: image IDs of snapshots taken of the droplet :vartype snapshot_ids: list of integers :var status: the current state of the droplet: ``"new"``, ``"active"``, ``"off"``, or ``"archive"`` :vartype status: string :var tags: tags that have been applied to the droplet :vartype tags: list of strings :var vcpus: number of virtual CPUs :vartype vcpus: int """ #: The status of droplets that are powered on and operating STATUS_ACTIVE = 'active' #: The status of "archived" droplets STATUS_ARCHIVE = 'archive' #: The status of recently-created droplets that are not yet usable STATUS_NEW = 'new' #: The status of droplets that are powered off STATUS_OFF = 'off' def __init__(self, state=None, **extra): super(Droplet, self).__init__(state, **extra) for attr, cls in [('image', Image), ('region', Region), ('size', Size), ('kernel', Kernel), ('networks', Networks), ('next_backup_window', BackupWindow)]: if self.get(attr) is not None and not isinstance(self[attr], cls): self[attr] = cls(self[attr], doapi_manager=self.doapi_manager) self.created_at = fromISO8601(self.get('created_at')) @property def active(self): """ `True` iff the droplet's status is ``"active"`` """ return self.status == self.STATUS_ACTIVE @property def new(self): """ `True` iff the droplet's status is ``"new"`` """ return self.status == self.STATUS_NEW @property def off(self): """ `True` iff the droplet's status is ``"off"`` """ return self.status == self.STATUS_OFF @property def archive(self): """ `True` iff the droplet's status is ``"archive"`` """ return self.status == self.STATUS_ARCHIVE @property def region_slug(self): """ The unique slug identifier for the droplet's region """ try: return self.region.slug except (TypeError, AttributeError): return None @property def image_slug(self): """ The unique slug identifier for the droplet's image, or `None` if the image doesn't have a slug """ try: return self.image.slug except (TypeError, AttributeError): return None @property def ip_address(self): """ The IP address of the first interface listed in the droplet's ``networks`` field (ordering IPv4 before IPv6), or `None` if there are no interfaces """ networks = self.get("networks", {}) v4nets = networks.get("v4", []) v6nets = networks.get("v6", []) try: return (v4nets + v6nets)[0].ip_address except IndexError: return None @property def ipv4_address(self): """ .. versionadded:: 0.2.0 The IP address of the first IPv4 interface listed in the droplet's ``networks`` field, or `None` if there is no IPv4 interface """ try: return self.networks.v4[0].ip_address except (AttributeError, LookupError): return None @property def ipv6_address(self): """ .. versionadded:: 0.2.0 The IP address of the first IPv6 interface listed in the droplet's ``networks`` field, or `None` if there is no IPv4 interface """ try: return self.networks.v6[0].ip_address except (AttributeError, LookupError): return None @property def url(self): """ The endpoint for general operations on the individual droplet """ return self._url('/v2/droplets/' + str(self.id))
[docs] def fetch(self): """ Fetch & return a new `Droplet` object representing the droplet's current state :rtype: Droplet :raises DOAPIError: if the API endpoint replies with an error (e.g., if the droplet no longer exists) """ api = self.doapi_manager return api._droplet(api.request(self.url)["droplet"])
[docs] def fetch_all_neighbors(self): r""" Returns a generator that yields all of the droplets running on the same physical server as the droplet :rtype: generator of `Droplet`\ s :raises DOAPIError: if the API endpoint replies with an error """ api = self.doapi_manager return map(api._droplet, api.paginate(self.url + '/neighbors', 'droplets'))
[docs] def fetch_all_snapshots(self): r""" Returns a generator that yields all of the snapshot images created from the droplet :rtype: generator of `Image`\ s :raises DOAPIError: if the API endpoint replies with an error """ api = self.doapi_manager for obj in api.paginate(self.url + '/snapshots', 'snapshots'): yield Image(obj, doapi_manager=api)
[docs] def fetch_all_backups(self): r""" Returns a generator that yields all of the backup images created from the droplet :rtype: generator of `Image`\ s :raises DOAPIError: if the API endpoint replies with an error """ api = self.doapi_manager for obj in api.paginate(self.url + '/backups', 'backups'): yield Image(obj, doapi_manager=api)
[docs] def fetch_all_kernels(self): r""" Returns a generator that yields all of the kernels available to the droplet :rtype: generator of `Kernel`\ s :raises DOAPIError: if the API endpoint replies with an error """ api = self.doapi_manager for kern in api.paginate(self.url + '/kernels', 'kernels'): yield Kernel(kern, doapi_manager=api)
[docs] def enable_backups(self): """ Enable backups on the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='enable_backups')
[docs] def disable_backups(self): """ Disable backups on the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='disable_backups')
[docs] def reboot(self): """ Reboot the droplet A reboot action is an attempt to reboot the Droplet in a graceful way, similar to using the :command:`reboot` command from the console. [APIDocs]_ :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='reboot')
[docs] def power_cycle(self): """ Power cycle the droplet A powercycle action is similar to pushing the reset button on a physical machine, it's similar to booting from scratch. [APIDocs]_ :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='power_cycle')
[docs] def shutdown(self): """ Shut down the droplet A shutdown action is an attempt to shutdown the Droplet in a graceful way, similar to using the :command:`shutdown` command from the console. Since a ``shutdown`` command can fail, this action guarantees that the command is issued, not that it succeeds. The preferred way to turn off a Droplet is to attempt a shutdown, with a reasonable timeout, followed by a power off action to ensure the Droplet is off. [APIDocs]_ :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='shutdown')
[docs] def power_off(self): """ Power off the droplet A ``power_off`` event is a hard shutdown and should only be used if the :meth:`shutdown` action is not successful. It is similar to cutting the power on a server and could lead to complications. [APIDocs]_ :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='power_off')
[docs] def power_on(self): """ Power on the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='power_on')
[docs] def restore(self, image): """ Restore the droplet to the specified backup image A Droplet restoration will rebuild an image using a backup image. The image ID that is passed in must be a backup of the current Droplet instance. The operation will leave any embedded SSH keys intact. [APIDocs]_ :param image: an image ID, an image slug, or an `Image` object representing a backup image of the droplet :type image: integer, string, or `Image` :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ if isinstance(image, Image): image = image.id return self.act(type='restore', image=image)
[docs] def password_reset(self): """ Reset the password for the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='password_reset')
[docs] def resize(self, size, disk=None): """ Resize the droplet :param size: a size slug or a `Size` object representing the size to resize to :type size: string or `Size` :param bool disk: Set to `True` for a permanent resize, including disk changes :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ if isinstance(size, Size): size = size.slug opts = {"disk": disk} if disk is not None else {} return self.act(type='resize', size=size, **opts)
[docs] def rebuild(self, image): """ Rebuild the droplet with the specified image A rebuild action functions just like a new create. [APIDocs]_ :param image: an image ID, an image slug, or an `Image` object representing the image the droplet should use as a base :type image: integer, string, or `Image` :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ if isinstance(image, Image): image = image.id return self.act(type='rebuild', image=image)
[docs] def rename(self, name): """ Rename the droplet :param str name: the new name for the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='rename', name=name)
[docs] def change_kernel(self, kernel): """ Change the droplet's kernel :param kernel: a kernel ID or `Kernel` object representing the new kernel :type kernel: integer or `Kernel` :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ if isinstance(kernel, Kernel): kernel = kernel.id return self.act(type='change_kernel', kernel=kernel)
[docs] def enable_ipv6(self): """ Enable IPv6 networking on the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='enable_ipv6')
[docs] def enable_private_networking(self): """ Enable private networking on the droplet :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='enable_private_networking')
[docs] def snapshot(self, name): """ Create a snapshot image of the droplet :param str name: the name for the new snapshot :return: an `Action` representing the in-progress operation on the droplet :rtype: Action :raises DOAPIError: if the API endpoint replies with an error """ return self.act(type='snapshot', name=name)
[docs] def delete(self): """ Delete the droplet :return: `None` :raises DOAPIError: if the API endpoint replies with an error """ self.doapi_manager.request(self.url, method='DELETE')
[docs] def wait(self, status=None, locked=None, wait_interval=None, wait_time=None): """ Poll the server periodically until the droplet has reached some final state. If ``status`` is non-`None`, ``wait`` will wait for the droplet's ``status`` field to equal the given value. If ``locked`` is non-`None`, `wait` will wait for the droplet's ``locked`` field to equal (the truth value of) the given value. Exactly one of ``status`` and ``locked`` must be non-`None`. If ``wait_time`` is exceeded, a `WaitTimeoutError` (containing the droplet's most recently fetched state) is raised. If a `KeyboardInterrupt` is caught, the droplet's most recently fetched state is returned immediately without waiting for completion. .. versionchanged:: 0.2.0 Raises `WaitTimeoutError` on timeout .. versionchanged:: 0.2.0 ``locked`` parameter added .. versionchanged:: 0.2.0 No longer waits for latest action to complete :param status: When non-`None`, the desired value for the ``status`` field of the droplet, which should be one of `Droplet.STATUS_ACTIVE`, `Droplet.STATUS_ARCHIVE`, `Droplet.STATUS_NEW`, and `Droplet.STATUS_OFF`. (For the sake of forwards-compatibility, any other value is accepted as well.) :type status: string or `None` :param locked: When non-`None`, the desired value for the ``locked`` field of the droplet :type locked: `bool` or `None` :param number wait_interval: how many seconds to sleep between requests; defaults to the `doapi` object's :attr:`~doapi.wait_interval` if not specified or `None` :param number wait_time: the total number of seconds after which the method will raise an error if the droplet has not yet completed, or a negative number to wait indefinitely; defaults to the `doapi` object's :attr:`~doapi.wait_time` if not specified or `None` :return: the droplet's final state :rtype: Droplet :raises TypeError: if both or neither of ``status`` & ``locked`` are defined :raises DOAPIError: if the API endpoint replies with an error :raises WaitTimeoutError: if ``wait_time`` is exceeded """ return next(self.doapi_manager.wait_droplets([self], status, locked, wait_interval, wait_time))
def _taggable(self): return {"resource_id": self.id, "resource_type": "droplet"}