Ejemplo n.º 1
0
    def ip_allocate(self, public=False):
        """
        Allocates a new :any:`IPAddress` for this Instance.  Additional public
        IPs require justification, and you may need to open a :any:`SupportTicket`
        before you can add one.  You may only have, at most, one private IP per
        Instance.

        :param public: If the new IP should be public or private.  Defaults to
                       private.
        :type public: bool

        :returns: The new IPAddress
        :rtype: IPAddress
        """
        result = self._client.post("{}/ips".format(Instance.api_endpoint),
                                   model=self,
                                   data={
                                       "type": "ipv4",
                                       "public": public,
                                   })

        if not 'address' in result:
            raise UnexpectedResponseError('Unexpected response allocating IP!',
                                          json=result)

        i = IPAddress(self._client, result['address'], result)
        return i
    def _get_objects(self,
                     endpoint,
                     cls,
                     model=None,
                     parent_id=None,
                     filters=None):
        response_json = self.get(endpoint, model=model, filters=filters)

        if not "data" in response_json:
            raise UnexpectedResponseError("Problem with response!",
                                          json=response_json)

        if 'pages' in response_json:
            formatted_endpoint = endpoint
            if model:
                formatted_endpoint = formatted_endpoint.format(**vars(model))
            return PaginatedList.make_paginated_list(
                response_json,
                self,
                cls,
                parent_id=parent_id,
                page_url=formatted_endpoint[1:],
                filters=filters)
        return PaginatedList.make_list(response_json["data"],
                                       self,
                                       cls,
                                       parent_id=parent_id)
Ejemplo n.º 3
0
    def clone(self, to_linode=None, region=None, service=None, configs=[], disks=[],
            label=None, group=None, with_backups=None):
        """ Clones this linode into a new linode or into a new linode in the given region """
        if to_linode and region:
            raise ValueError('You may only specify one of "to_linode" and "region"')

        if region and not service:
            raise ValueError('Specifying a region requires a "service" as well')

        if not isinstance(configs, list) and not isinstance(configs, PaginatedList):
            configs = [configs]
        if not isinstance(disks, list) and not isinstance(disks, PaginatedList):
            disks = [disks]

        cids = [ c.id if issubclass(type(c), Base) else c for c in configs ]
        dids = [ d.id if issubclass(type(d), Base) else d for d in disks ]

        params = {
            "linode_id": to_linode.id if issubclass(type(to_linode), Base) else to_linode,
            "region": region.id if issubclass(type(region), Base) else region,
            "type": service.id if issubclass(type(service), Base) else service,
            "configs": cids if cids else None,
            "disks": dids if dids else None,
            "label": label,
            "group": group,
            "with_backups": with_backups,
        }

        result = self._client.post('{}/clone'.format(Instance.api_endpoint), model=self, data=params)

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response cloning Instance!', json=result)

        l = Instance(self._client, result['id'], result)
        return l
Ejemplo n.º 4
0
    def available_backups(self):
        """
        The backups response contains what backups are available to be restored.
        """
        if not hasattr(self, '_avail_backups'):
            result = self._client.get("{}/backups".format(Instance.api_endpoint), model=self)

            if not 'automatic' in result:
                raise UnexpectedResponseError('Unexpected response loading available backups!', json=result)

            automatic = []
            for a in result['automatic']:
                cur = Backup(self._client, a['id'], self.id, a)
                automatic.append(cur)

            snap = None
            if result['snapshot']['current']:
                snap = Backup(self._client, result['snapshot']['current']['id'], self.id,
                        result['snapshot']['current'])

            psnap = None
            if result['snapshot']['in_progress']:
                psnap = Backup(self._client, result['snapshot']['in_progress']['id'], self.id,
                        result['snapshot']['in_progress'])

            self._set('_avail_backups', MappedObject(**{
                "automatic": automatic,
                "snapshot": {
                    "current": snap,
                    "in_progress": psnap,
                }
            }))

        return self._avail_backups
    def image_create(self, disk, label=None, description=None):
        """
        Creates a new Image from a disk you own.

        :param disk: The Disk to imagize.
        :type disk: Disk or int
        :param label: The label for the resulting Image (defaults to the disk's
                      label.
        :type label: str
        :param description: The description for the new Image.
        :type description: str

        :returns: The new Image.
        :rtype: Image
        """
        params = {
            "disk_id": disk.id if issubclass(type(disk), Base) else disk,
        }

        if label is not None:
            params["label"] = label

        if description is not None:
            params["description"] = description

        result = self.post('/images', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when creating an '
                'Image from disk {}'.format(disk))

        return Image(self, result['id'], result)
    def domain_create(self, domain, master=True, **kwargs):
        """
        Registers a new Domain on the acting user's account.  Make sure to point
        your registrar to Linode's nameservers so that Linode's DNS manager will
        correctly serve your domain.

        :param domain: The domain to register to Linode's DNS manager.
        :type domain: str
        :param master: Whether this is a master (defaults to true)
        :type master: bool

        :returns: The new Domain object.
        :rtype: Domain
        """
        params = {
            'domain': domain,
            'type': 'master' if master else 'slave',
        }
        params.update(kwargs)

        result = self.post('/domains', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when creating Domain!', json=result)

        d = Domain(self, result['id'], result)
        return d
    def ip_allocate(self, linode, public=True):
        """
        Allocates an IP to a Instance you own.  Additional IPs must be requested
        by opening a support ticket first.

        :param linode: The Instance to allocate the new IP for.
        :type linode: Instance or int
        :param public: If True, allocate a public IP address.  Defaults to True.
        :type public: bool

        :returns: The new IPAddress
        :rtype: IPAddress
        """
        result = self.client.post(
            '/networking/ips/',
            data={
                "linode_id": linode.id if isinstance(linode, Base) else linode,
                "type": "ipv4",
                "public": public,
            })

        if not 'address' in result:
            raise UnexpectedResponseError(
                'Unexpected response when adding IPv4 address!', json=result)

        ip = IPAddress(self.client, result['address'], result)
        return ip
Ejemplo n.º 8
0
    def node_pool_create(self, node_type, node_count, **kwargs):
        """
        Creates a new :any:`LKENodePool` for this cluster.

        :param node_type: The type of nodes to create in this pool.
        :type node_type: :any:`Type` or str
        :param node_count: The number of nodes to create in this pool.
        :type node_count: int
        :param kwargs: Any other arguments to pass to the API.  See the API docs
                       for possible values.

        :returns: The new Node Pool
        :rtype: LKENodePool
        """
        params = {
            "type": node_type,
            "count": node_count,
        }
        params.update(kwargs)

        result = self._client.post("{}/pools".format(LKECluster.api_endpoint),
                                   model=self,
                                   data=params)
        self.invalidate()

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response creating node pool!', json=result)

        return LKENodePool(self._client, result["id"], self.id, result)
    def ticket_open(self, summary, description, regarding=None):
        """

        """
        params = {
            "summary": summary,
            "description": description,
        }

        if regarding:
            if isinstance(regarding, Instance):
                params['linode_id'] = regarding.id
            elif isinstance(regarding, Domain):
                params['domain_id'] = regarding.id
            elif isinstance(regarding, NodeBalancer):
                params['nodebalancer_id'] = regarding.id
            elif isinstance(regarding, Volume):
                params['volume_id'] = regarding.id
            else:
                raise ValueError(
                    'Cannot open ticket regarding type {}!'.format(
                        type(regarding)))

        result = self.client.post('/support/tickets', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when creating ticket!', json=result)

        t = SupportTicket(self.client, result['id'], result)
        return t
Ejemplo n.º 10
0
    def stackscript_create(self, label, script, images, desc=None, public=False, **kwargs):
        """
        Creates a new :any:`StackScript` on your account.

        :param label: The label for this StackScript.
        :type label: str
        :param script: The script to run when an :any:`Instance` is deployed with
                       this StackScript.  Must begin with a shebang (#!).
        :type script: str
        :param images: A list of :any:`Images<Image>` that this StackScript
                       supports.  Instances will not be deployed from this
                       StackScript unless deployed from one of these Images.
        :type images: list of Image
        :param desc: A description for this StackScript.
        :type desc: str
        :param public: Whether this StackScript is public.  Defaults to False.
                       Once a StackScript is made public, it may not be set
                       back to private.
        :type public: bool

        :returns: The new StackScript
        :rtype: StackScript
        """
        image_list = None
        if type(images) is list or type(images) is PaginatedList:
            image_list = [d.id if issubclass(type(d), Base) else d for d in images ]
        elif type(images) is Image:
            image_list = [images.id]
        elif type(images) is str:
            image_list = [images]
        else:
            raise ValueError('images must be a list of Images or a single Image')

        script_body = script
        if not script.startswith("#!"):
            # it doesn't look like a stackscript body, let's see if it's a file
            import os
            if os.path.isfile(script):
                with open(script) as f:
                    script_body = f.read()
            else:
                raise ValueError("script must be the script text or a path to a file")

        params = {
            "label": label,
            "images": image_list,
            "is_public": public,
            "script": script_body,
            "description": desc if desc else '',
        }
        params.update(kwargs)

        result = self.client.post('/linode/stackscripts', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response when creating StackScript!', json=result)

        s = StackScript(self.client, result['id'], result)
        return s
Ejemplo n.º 11
0
    def duplicate(self):
        result = self._client.post(Disk.api_endpoint, model=self, data={})

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response duplicating disk!', json=result)

        d = Disk(self._client, result['id'], self.linode_id, result)
        return d
Ejemplo n.º 12
0
    def transfer(self):
        """
        Returns a MappedObject containing the account's transfer pool data
        """
        result = self.client.get('/account/transfer')

        if not 'used' in result:
            raise UnexpectedResponseError('Unexpected response when getting Transfer Pool!')

        return MappedObject(**result)
Ejemplo n.º 13
0
    def ips(self):
        """
        The ips related collection is not normalized like the others, so we have to
        make an ad-hoc object to return for its response
        """
        if not hasattr(self, '_ips'):
            result = self._client.get("{}/ips".format(Instance.api_endpoint),
                                      model=self)

            if not "ipv4" in result:
                raise UnexpectedResponseError(
                    'Unexpected response loading IPs', json=result)

            v4pub = []
            for c in result['ipv4']['public']:
                i = IPAddress(self._client, c['address'], c)
                v4pub.append(i)

            v4pri = []
            for c in result['ipv4']['private']:
                i = IPAddress(self._client, c['address'], c)
                v4pri.append(i)

            shared_ips = []
            for c in result['ipv4']['shared']:
                i = IPAddress(self._client, c['address'], c)
                shared_ips.append(i)

            slaac = IPAddress(self._client, result['ipv6']['slaac']['address'],
                              result['ipv6']['slaac'])
            link_local = IPAddress(self._client,
                                   result['ipv6']['link_local']['address'],
                                   result['ipv6']['link_local'])

            pools = []
            for p in result['ipv6']['global']:
                pools.append(IPv6Pool(self._client, p['range']))

            ips = MappedObject(
                **{
                    "ipv4": {
                        "public": v4pub,
                        "private": v4pri,
                        "shared": shared_ips,
                    },
                    "ipv6": {
                        "slaac": slaac,
                        "link_local": link_local,
                        "pools": pools,
                    },
                })

            self._set('_ips', ips)

        return self._ips
Ejemplo n.º 14
0
    def config_create(self, label=None, **kwargs):
        params = kwargs
        if label:
            params['label'] = label

        result = self._client.post("{}/configs".format(NodeBalancer.api_endpoint), model=self, data=params)
        self.invalidate()

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response creating config!', json=result)

        c = NodeBalancerConfig(self._client, result['id'], self.id, result)
        return c
Ejemplo n.º 15
0
    def post_reply(self, description):
        """
        """
        result = self._client.post("{}/replies".format(SupportTicket.api_endpoint), model=self, data={
            "description": description,
        })

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response when creating ticket reply!',
                    json=result)

        r = TicketReply(self._client, result['id'], self.id, result)
        return r
Ejemplo n.º 16
0
    def snapshot(self, label=None):
        result = self._client.post("{}/backups".format(Instance.api_endpoint), model=self,
                                   data={ "label": label })

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response taking snapshot!', json=result)

        # so the changes show up the next time they're accessed
        if hasattr(self, '_avail_backups'):
            del self._avail_backups

        b = Backup(self._client, result['id'], self.id, result)
        return b
Ejemplo n.º 17
0
    def settings(self):
        """
        Resturns the account settings data for this acocunt.  This is not  a
        listing endpoint.
        """
        result = self.client.get('/account/settings')

        if not 'managed' in result:
            raise UnexpectedResponseError('Unexpected response when getting account settings!',
                    json=result)

        s = AccountSettings(self.client, result['managed'], result)
        return s
Ejemplo n.º 18
0
    def reset_secret(self):
        """
        Resets the client secret for this client.
        """
        result = self._client.post("{}/reset_secret".format(
            OAuthClient.api_endpoint),
                                   model=self)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when resetting secret!', json=result)

        self._populate(result)
        return self.secret
Ejemplo n.º 19
0
    def rebuild(self, image, root_pass=None, authorized_keys=None, **kwargs):
        """
        Rebuilding an Instance deletes all existing Disks and Configs and deploys
        a new :any:`Image` to it.  This can be used to reset an existing
        Instance or to install an Image on an empty Instance.

        :param image: The Image to deploy to this Instance
        :type image: str or Image
        :param root_pass: The root password for the newly rebuilt Instance.  If
                          omitted, a password will be generated and returned.
        :type root_pass: str
        :param authorized_keys: The ssh public keys to install in the linode's
                                /root/.ssh/authorized_keys file.  Each entry may
                                be a single key, or a path to a file containing
                                the key.
        :type authorized_keys: list or str

        :returns: The newly generated password, if one was not provided
                  (otherwise True)
        :rtype: str or bool
        """
        ret_pass = None
        if not root_pass:
            ret_pass = Instance.generate_root_password()
            root_pass = ret_pass

        authorized_keys = load_and_validate_keys(authorized_keys)

        params = {
            'image': image.id if issubclass(type(image), Base) else image,
            'root_pass': root_pass,
            'authorized_keys': authorized_keys,
        }
        params.update(kwargs)

        result = self._client.post('{}/rebuild'.format(Instance.api_endpoint),
                                   model=self,
                                   data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response issuing rebuild!', json=result)

        # update ourself with the newly-returned information
        self._populate(result)

        if not ret_pass:
            return True
        else:
            return ret_pass
Ejemplo n.º 20
0
    def attach(self, to_linode, config=None):
        """
        Attaches this Volume to the given Linode
        """
        result = self._client.post('{}/attach'.format(Volume.api_endpoint), model=self,
                data={
                    "linode_id": to_linode.id if issubclass(type(to_linode), Base) else to_linode,
                    "config": None if not config else config.id if issubclass(type(config), Base) else config,
        })

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response when attaching volume!', json=result)

        self._populate(result)
        return True
Ejemplo n.º 21
0
    def record_create(self, record_type, **kwargs):

        params = {
            "type": record_type,
        }
        params.update(kwargs)

        result = self._client.post("{}/records".format(Domain.api_endpoint), model=self, data=params)
        self.invalidate()

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response creating domain record!', json=result)

        zr = DomainRecord(self._client, result['id'], self.id, result)
        return zr
Ejemplo n.º 22
0
    def transfer(self):
        """
        Get per-linode transfer
        """
        if not hasattr(self, '_transfer'):
            result = self._client.get("{}/transfer".format(Instance.api_endpoint), model=self)

            if not 'used' in result:
                raise UnexpectedResponseError('Unexpected response when getting Transfer Pool!')

            mapped = MappedObject(**result)

            setattr(self, '_transfer', mapped)

        return self._transfer
Ejemplo n.º 23
0
    def clone(self, label):
        """
        Clones this volume to a new volume in the same region with the given label

        :param label: The label for the new volume.

        :returns: The new volume object.
        """
        result = self._client.post('{}/clone'.format(Volume.api_endpoint),
                model=self, data={'label': label})

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response cloning volume!')

        return Volume(self._client, result['id'], result)
Ejemplo n.º 24
0
    def add_whitelist_entry(self, address, netmask, note=None):
        """
        Adds a new entry to this user's IP whitelist, if enabled
        """
        result = self._client.post("{}/whitelist".format(Profile.api_endpoint),
                data={
                    "address": address,
                    "netmask": netmask,
                    "note": note,
        })

        if not 'id' in result:
            raise UnexpectedResponseError("Unexpected response creating whitelist entry!")

        return WhitelistEntry(result['id'], self._client, json=result)
Ejemplo n.º 25
0
    def node_create(self, label, address, **kwargs):
        params = {
            "label": label,
            "address": address,
        }
        params.update(kwargs)

        result = self._client.post("{}/nodes".format(NodeBalancerConfig.api_endpoint), model=self, data=params)
        self.invalidate()

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response creating node!', json=result)

        # this is three levels deep, so we need a special constructor
        n = NodeBalancerNode(self._client, result['id'], self.id, self.nodebalancer_id, result)
        return n
Ejemplo n.º 26
0
    def __call__(self):
        """
        Retrieves information about the acting user's account, such as billing
        information.  This is intended to be called off of the :any:`LinodeClient`
        class, like this::

           account = client.account()

        :returns: Returns the acting user's account information.
        :rtype: Account
        """
        result = self.client.get('/account')

        if not 'email' in result:
            raise UnexpectedResponseError('Unexpected response when getting account!', json=result)

        return Account(self.client, result['email'], result)
Ejemplo n.º 27
0
    def __call__(self):
        """
        Retrieve the acting user's Profile, containing information about the
        current user such as their email address, username, and uid.  This is
        intended to be called off of a :any:`LinodeClient` object, like this::

           profile = client.profile()

        :returns: The acting user's profile.
        :rtype: Profile
        """
        result = self.client.get('/profile')

        if not 'username' in result:
            raise UnexpectedResponseError('Unexpected response when getting profile!', json=result)

        p = Profile(self, result['username'], result)
        return p
    def oauth_client_create(self, name, redirect_uri, **kwargs):
        """
        Make a new OAuth Client and return it
        """
        params = {
            "label": name,
            "redirect_uri": redirect_uri,
        }
        params.update(kwargs)

        result = self.client.post('/account/oauth-clients', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when creating OAuth Client!', json=result)

        c = OAuthClient(self.client, result['id'], result)
        return c
Ejemplo n.º 29
0
    def reset_root_password(self, root_password=None):
        rpass = root_password
        if not rpass:
            rpass = Instance.generate_root_password()

        params = {
            'password': rpass,
        }

        result = self._client.post(Disk.api_endpoint, model=self, data=params)

        if not 'id' in result:
            raise UnexpectedResponseError('Unexpected response duplicating disk!', json=result)

        self._populate(result)
        if not root_password:
            return True, rpass
        return True
    def volume_create(self,
                      label,
                      region=None,
                      linode=None,
                      size=20,
                      **kwargs):
        """
        Creates a new Block Storage Volume, either in the given Region or
        attached to the given Instance.

        :param label: The label for the new Volume.
        :type label: str
        :param region: The Region to create this Volume in.  Not required if
                       `linode` is provided.
        :type region: Region or str
        :param linode: The Instance to attach this Volume to.  If not given, the
                       new Volume will not be attached to anything.
        :type linode: Instance or int
        :param size: The size, in GB, of the new Volume.  Defaults to 20.
        :type size: int

        :returns: The new Volume.
        :rtype: Volume
        """
        if not (region or linode):
            raise ValueError('region or linode required!')

        params = {
            "label": label,
            "size": size,
            "region": region.id if issubclass(type(region), Base) else region,
            "linode_id":
            linode.id if issubclass(type(linode), Base) else linode,
        }
        params.update(kwargs)

        result = self.post('/volumes', data=params)

        if not 'id' in result:
            raise UnexpectedResponseError(
                'Unexpected response when creating volume!', json=result)

        v = Volume(self, result['id'], result)
        return v