Beispiel #1
0
class User(Base):
    api_endpoint = "/account/users/{id}"
    id_attribute = 'username'

    properties = {
        'email': Property(mutable=True),
        'username': Property(identifier=True, mutable=True),
        'restricted': Property(mutable=True),
    }

    @property
    def grants(self):
        from linode.objects.account import UserGrants
        if not hasattr(self, '_grants'):
            resp = self._client.get(UserGrants.api_endpoint.format(username=self.username))

            grants = UserGrants(self._client, self.username, resp)
            self._set('_grants', grants)

        return self._grants

    def invalidate(self):
        if hasattr(self, '_grants'):
            del self._grants
        Base.invalidate(self)

    def change_password(self, password):
        """
        Sets this user's password
        """
        result = self._client.post('{}/password'.format(User.api_endpoint),
                model=self, data={ "password": password })

        return True
Beispiel #2
0
class User(Base):
    api_endpoint = "/account/users/{id}"
    id_attribute = 'username'

    properties = {
        'email': Property(),
        'username': Property(identifier=True, mutable=True),
        'restricted': Property(mutable=True),
    }

    @property
    def grants(self):
        """
        Retrieves the grants for this user.  If the user is unrestricted, this
        will result in an ApiError.  This is smart, and will only fetch from the
        api once unless the object is invalidated.

        :returns: The grants for this user.
        :rtype: linode.objects.account.UserGrants
        """
        from linode.objects.account import UserGrants
        if not hasattr(self, '_grants'):
            resp = self._client.get(
                UserGrants.api_endpoint.format(username=self.username))

            grants = UserGrants(self._client, self.username, resp)
            self._set('_grants', grants)

        return self._grants

    def invalidate(self):
        if hasattr(self, '_grants'):
            del self._grants
        Base.invalidate(self)
Beispiel #3
0
class IPv6Pool(Base):
    api_endpoint = '/networking/ipv6/pools/{}'
    id_attribute = 'range'

    properties = {
        'range': Property(identifier=True),
        'region': Property(slug_relationship=Region, filterable=True),
    }
Beispiel #4
0
class LongviewSubscription(Base):
    api_endpoint = 'longview/subscriptions/{id}'
    properties = {
        "id": Property(identifier=True),
        "label": Property(),
        "clients_included": Property(),
        "price": Property()
    }
Beispiel #5
0
class Payment(Base):
    api_endpoint = "/account/payments/{id}"

    properties = {
        "id": Property(identifier=True),
        "date": Property(is_datetime=True),
        "amount": Property(),
    }
class WhitelistEntry(Base):
    api_endpoint = "/profile/whitelist/{id}"

    properties = {
        'id': Property(identifier=True),
        'address': Property(),
        'netmask': Property(),
        'note': Property(),
    }
Beispiel #7
0
class Invoice(Base):
    api_endpoint = "/account/invoices/{id}"

    properties = {
        "id": Property(identifier=True),
        "label": Property(),
        "date": Property(is_datetime=True),
        "total": Property(),
    }
Beispiel #8
0
class AccountSettings(Base):
    api_endpoint = "/account/settings"
    id_attribute = 'managed'  # this isn't actually used

    properties = {
        "network_helper": Property(mutable=True),
        "managed": Property(),
        "longview_subscription":
        Property(slug_relationship=LongviewSubscription)
    }
Beispiel #9
0
class IPAddress(Base):
    api_endpoint = '/networking/ips/{address}'
    id_attribute = 'address'

    properties = {
        "address": Property(identifier=True),
        "gateway": Property(),
        "subnet_mask": Property(),
        "prefix": Property(),
        "type": Property(),
        "public": Property(),
        "rdns": Property(mutable=True),
        "linode_id": Property(),
        "region": Property(slug_relationship=Region, filterable=True),
    }

    @property
    def linode(self):
        from ..linode import Linode
        if not hasattr(self, '_linode'):
            self._set('_linode', Linode(self._client, self.linode_id))
        return self._linode

    def to(self, linode):
        """
        This is a helper method for ip-assign, and should not be used outside
        of that context.  It's used to cleanly build an IP Assign request with
        pretty python syntax.
        """
        from ..linode import Linode
        if not isinstance(linode, Linode):
            raise ValueError("IP Address can only be assigned to a Linode!")
        return {"address": self.address, "linode_id": linode.id}
Beispiel #10
0
class Kernel(Base):
    api_endpoint = "/linode/kernels/{id}"
    properties = {
        "created": Property(is_datetime=True),
        "deprecated": Property(filterable=True),
        "description": Property(),
        "id": Property(identifier=True),
        "kvm": Property(filterable=True),
        "label": Property(filterable=True),
        "updates": Property(),
        "version": Property(filterable=True),
        "architecture": Property(filterable=True),
        "xen": Property(filterable=True),
    }
Beispiel #11
0
class NodeBalancerNode(DerivedBase):
    api_endpoint = '/nodebalancers/{nodebalancer_id}/configs/{config_id}/nodes/{id}'
    derived_url_path = 'nodes'
    parent_id_name = 'config_id'

    properties = {
        'id': Property(identifier=True),
        'config_id': Property(identifier=True),
        'nodebalancer_id': Property(identifier=True),
        "label": Property(mutable=True),
        "address": Property(mutable=True),
        "weight": Property(mutable=True),
        "mode": Property(mutable=True),
        "status": Property(),
    }

    def __init__(self, client, id, parent_id, nodebalancer_id=None, json=None):
        """
        We need a special constructor here because this object's parent
        has a parent itself.
        """
        if not nodebalancer_id and not isinstance(parent_id, tuple):
            raise ValueError(
                'NodeBalancerNode must either be created with a nodebalancer_id or a tuple of '
                '(config_id, nodebalancer_id) for parent_id!')

        if isinstance(parent_id, tuple):
            nodebalancer_id = parent_id[1]
            parent_id = parent_id[0]

        DerivedBase.__init__(self, client, id, parent_id, json=json)

        self._set('nodebalancer_id', nodebalancer_id)
class Type(Base):
    api_endpoint = "/linode/types/{id}"
    properties = {
        'disk': Property(filterable=True),
        'id': Property(identifier=True),
        'label': Property(filterable=True),
        'network_out': Property(filterable=True),
        'price': Property(),
        'addons': Property(),
        'memory': Property(filterable=True),
        'transfer': Property(filterable=True),
        'vcpus': Property(filterable=True),
    }
Beispiel #13
0
class IPv6Address(Base):
    api_endpoint = 'networking/ipv6/{address}'
    id_attribute = 'address'

    properties = {
        "address": Property(identifier=True),
        "gateway": Property(),
        "range": Property(),
        "rdns": Property(mutable=True),
        "prefix": Property(),
        "subnet_mask": Property(),
        "type": Property(),
        "region": Property(slug_relationship=Region),
    }
Beispiel #14
0
class OAuthToken(Base):
    api_name = 'tokens'
    api_endpoint = "/account/tokens/{id}"

    properties = {
        "id": Property(identifier=True),
        "client": Property(relationship=OAuthClient),
        "type": Property(),
        "scopes": Property(),
        "label": Property(mutable=True),
        "created": Property(is_datetime=True),
        "token": Property(),
        "expiry": Property(is_datetime=True),
    }
Beispiel #15
0
class AuthorizedApp(Base):
    api_endpoint = "/profile/apps/{id}"

    properties = {
        "id": Property(identifier=True),
        "scopes": Property(),
        "label": Property(),
        "created": Property(is_datetime=True),
        "expiry": Property(is_datetime=True),
        "thumbnail_url": Property(),
        "website": Property(),
    }
Beispiel #16
0
class LongviewClient(Base):

    api_endpoint = '/longview/clients/{id}'

    properties = {
        "id": Property(identifier=True),
        "created": Property(is_datetime=True),
        "updated": Property(is_datetime=True),
        "label": Property(mutable=True, filterable=True),
        "install_code": Property(),
        "apps": Property(),
        "api_key": Property(),
    }
class InvoiceItem(DerivedBase):
    api_endpoint = '/account/invoices/{invoice_id}/items'
    derived_url_path = 'items'
    parent_id_name = 'invoice_id'

    # TODO - this object doesn't have its own ID .. this might need
    # special handling
    properties = {
        'invoice_id': Property(identifier=True),
        'unit_price': Property(),
        'label': Property(),
        'amount': Property(),
        'quantity': Property(),
        'from': Property(is_datetime=True),
        'to': Property(is_datetime=True),
        'type': Property(),
    }
Beispiel #18
0
class Disk(DerivedBase):
    api_endpoint = '/linode/instances/{linode_id}/disks/{id}'
    derived_url_path = 'disks'
    parent_id_name = 'linode_id'

    properties = {
        'id': Property(identifier=True),
        'created': Property(is_datetime=True),
        'label': Property(mutable=True, filterable=True),
        'size': Property(filterable=True),
        'status': Property(filterable=True),
        'filesystem': Property(),
        'updated': Property(is_datetime=True),
        'linode_id': Property(identifier=True),
    }

    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

    def reset_root_password(self, root_password=None):
        rpass = root_password
        if not rpass:
            from linode.objects.linode import Linode
            rpass = Linode.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
class PersonalAccessToken(Base):
    api_endpoint = "/profile/tokens/{id}"

    properties = {
        "id": Property(identifier=True),
        "scopes": Property(),
        "label": Property(mutable=True),
        "created": Property(is_datetime=True),
        "token": Property(),
        "expiry": Property(is_datetime=True),
    }
Beispiel #20
0
class TicketReply(DerivedBase):
    api_endpoint = '/support/tickets/{ticket_id}/replies'
    derived_url_path = 'replies'
    parent_id_name = 'ticket_id'

    properties = {
        'id': Property(identifier=True),
        'ticket_id': Property(identifier=True),
        'description': Property(),
        'created': Property(is_datetime=True),
        'created_by': Property(),
        'from_linode': Property(),
    }
Beispiel #21
0
class StackScript(Base):
    api_endpoint = '/linode/stackscripts/{id}'
    properties = {
        "user_defined_fields": Property(),
        "label": Property(mutable=True, filterable=True),
        "rev_note": Property(mutable=True),
        "usernam": Property(filterable=True),
        "user_gravatar_id": Property(),
        "is_public": Property(mutable=True, filterable=True),
        "created": Property(is_datetime=True),
        "deployments_active": Property(),
        "script": Property(mutable=True),
        "images": Property(mutable=True,
                           filterable=True),  # TODO make slug_relationship
        "deployments_total": Property(),
        "description": Property(mutable=True, filterable=True),
        "updated": Property(is_datetime=True),
    }

    def _populate(self, json):
        """
        Override the populate method to map user_defined_fields to
        fancy values
        """
        Base._populate(self, json)

        mapped_udfs = []
        for udf in self.user_defined_fields:
            t = UserDefinedFieldType.text
            choices = None
            if hasattr(udf, 'oneof'):
                t = UserDefinedFieldType.select_one
                choices = udf.oneof.split(',')
            elif hasattr(udf, 'manyof'):
                t = UserDefinedFieldType.select_many
                choices = udf.manyof.split(',')

            mapped_udfs.append(
                UserDefinedField(
                    udf.name,
                    udf.label if hasattr(udf, 'label') else None,
                    udf.example if hasattr(udf, 'example') else None,
                    t,
                    choices=choices))

        self._set('user_defined_fields', mapped_udfs)
        ndist = [Image(self._client, d) for d in self.images]
        self._set('images', ndist)

    def _serialize(self):
        dct = Base._serialize(self)
        dct['images'] = [d.id for d in self.images]
        return dct
Beispiel #22
0
class AccountSettings(Base):
    api_name = 'settings'  # should never come up
    api_endpoint = "/account/settings"
    id_attribute = 'email'

    properties = {
        "company": Property(mutable=True),
        "country": Property(mutable=True),
        "balance": Property(),
        "address_1": Property(mutable=True),
        "network_helper": Property(mutable=True),
        "last_name": Property(mutable=True),
        "city": Property(mutable=True),
        "state": Property(mutable=True),
        "first_name": Property(mutable=True),
        "phone": Property(mutable=True),
        "managed": Property(),
        "email": Property(mutable=True),
        "zip": Property(mutable=True),
        "address_2": Property(mutable=True),
    }
Beispiel #23
0
class DomainRecord(DerivedBase):
    api_name = "records"
    api_endpoint = "/domains/{domain_id}/records/{id}"
    derived_url_path = "records"
    parent_id_name = "domain_id"

    properties = {
        'id': Property(identifier=True),
        'domain_id': Property(identifier=True),
        'type': Property(),
        'name': Property(mutable=True, filterable=True),
        'target': Property(mutable=True, filterable=True),
        'priority': Property(mutable=True),
        'weight': Property(mutable=True),
        'port': Property(mutable=True),
        'service': Property(mutable=True),
        'protocol': Property(mutable=True),
        'ttl_sec': Property(mutable=True),
    }
Beispiel #24
0
class Event(Base):
    api_endpoint = '/account/events/{id}'
    properties = {
        'id': Property(identifier=True),
        'percent_complete': Property(volatile=True),
        'created': Property(is_datetime=True, filterable=True),
        'updated': Property(is_datetime=True, filterable=True),
        'seen': Property(),
        'read': Property(),
        'action': Property(),
        'user_id': Property(),
        'username': Property(),
        'entity': Property(),
        'time_remaining': Property(),
        'rate': Property(),
        'status': Property(),
    }

    @property
    def linode(self):
        if self.entity and self.entity.type == 'linode':
            return Linode(self._client, self.entity.id)
        return None

    @property
    def stackscript(self):
        if self.entity and self.entity.type == 'stackscript':
            return StackScript(self._client, self.entity.id)
        return None

    @property
    def domain(self):
        if self.entity and self.entity.type == 'domain':
            return Domain(self._client, self.entity.id)
        return None

    @property
    def nodebalancer(self):
        if self.entity and self.entity.type == 'nodebalancer':
            return NodeBalancer(self._client, self.entity.id)
        return None

    @property
    def ticket(self):
        if self.entity and self.entity.type == 'ticket':
            return SupportTicket(self._client, self.entity.id)
        return None

    @property
    def volume(self):
        if self.entity and self.entity.type == 'volume':
            return Volume(self._client, self.entity.id)
        return None

    def mark_read(self):
        self._client.post('{}/read'.format(Event.api_endpoint), model=self)
Beispiel #25
0
class Account(Base):
    api_endpoint = "/account"
    id_attribute = 'email'

    properties = {
        "company": Property(mutable=True),
        "country": Property(mutable=True),
        "balance": Property(),
        "address_1": Property(mutable=True),
        "last_name": Property(mutable=True),
        "city": Property(mutable=True),
        "state": Property(mutable=True),
        "first_name": Property(mutable=True),
        "phone": Property(mutable=True),
        "email": Property(mutable=True),
        "zip": Property(mutable=True),
        "address_2": Property(mutable=True),
        "tax_id": Property(mutable=True),
    }
Beispiel #26
0
class Domain(Base):
    api_endpoint = "/domains/{id}"
    properties = {
        'id': Property(identifier=True),
        'domain': Property(mutable=True, filterable=True),
        'group': Property(mutable=True, filterable=True),
        'description': Property(mutable=True),
        'status': Property(mutable=True),
        'soa_email': Property(mutable=True),
        'retry_sec': Property(mutable=True),
        'master_ips': Property(mutable=True, filterable=True),
        'axfr_ips': Property(mutable=True),
        'expire_sec': Property(mutable=True),
        'refresh_sec': Property(mutable=True),
        'ttl_sec': Property(mutable=True),
        'records': Property(derived_class=DomainRecord),
        'type': Property(mutable=True),
    }

    def create_record(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
Beispiel #27
0
class Linode(Base):
    api_endpoint = '/linode/instances/{id}'
    properties = {
        'id': Property(identifier=True, filterable=True),
        'label': Property(mutable=True, filterable=True),
        'group': Property(mutable=True, filterable=True),
        'status': Property(volatile=True),
        'created': Property(is_datetime=True),
        'updated': Property(volatile=True, is_datetime=True),
        'region': Property(slug_relationship=Region, filterable=True),
        'alerts': Property(),
        'image': Property(slug_relationship=Image, filterable=True),
        'disks': Property(derived_class=Disk),
        'configs': Property(derived_class=Config),
        'type': Property(slug_relationship=Type),
        'backups': Property(),
        'ipv4': Property(),
        'ipv6': Property(),
        'hypervisor': Property(),
        'specs': Property(),
    }

    @property
    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(Linode.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

    @property
    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(Linode.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 _populate(self, json):
        if json is not None:
            # fixes ipv4 and ipv6 attribute of json to make base._populate work
            if 'ipv4' in json and 'address' in json['ipv4']:
                json['ipv4']['id'] = json['ipv4']['address']
            if 'ipv6' in json and isinstance(json['ipv6'], list):
                for j in json['ipv6']:
                    j['id'] = j['range']

        Base._populate(self, json)

    def invalidate(self):
        """ Clear out cached properties """
        if hasattr(self, '_avail_backups'):
            del self._avail_backups
        if hasattr(self, '_ips'):
            del self._ips

        Base.invalidate(self)

    def boot(self, config=None):
        resp = self._client.post("{}/boot".format(Linode.api_endpoint), model=self, data={'config_id': config.id} if config else None)

        if 'error' in resp:
            return False
        return True

    def shutdown(self):
        resp = self._client.post("{}/shutdown".format(Linode.api_endpoint), model=self)

        if 'error' in resp:
            return False
        return True

    def reboot(self):
        resp = self._client.post("{}/reboot".format(Linode.api_endpoint), model=self)

        if 'error' in resp:
            return False
        return True

    @staticmethod
    def generate_root_password():
        def _func(value):
            if sys.version_info[0] < 3:
                value = int(value.encode('hex'), 16)
            return value

        password = ''.join([
            PASSWORD_CHARS[_func(c) % len(PASSWORD_CHARS)]
            for c in urandom(randint(50, 128))
        ])

        return password

    # create derived objects
    def create_config(self, kernel=None, label=None, devices=[], disks=[],
            volumes=[], **kwargs):
        """
        Creates a Linode Config with the given attributes.

        :param kernel: The kernel to boot with.
        :param label: The config label
        :param disks: The list of disks, starting at sda, to map to this config.
        :param volumes: The volumes, starting after the last disk, to map to this
            config
        :param devices: A list of devices to assign to this config, in device
            index order.  Values must be of type Disk or Volume. If this is
            given, you may not include disks or volumes.
        :param **kwargs: Any other arguments accepted by the api.

        :returns: A new Linode Config
        """
        from ..volume import Volume

        hypervisor_prefix = 'sd' if self.hypervisor == 'kvm' else 'xvd'
        device_names = [hypervisor_prefix + string.ascii_lowercase[i] for i in range(0, 8)]
        device_map = {device_names[i]: None for i in range(0, len(device_names))}

        if devices and (disks or volumes):
            raise ValueError('You may not call create_config with "devices" and '
                    'either of "disks" or "volumes" specified!')

        if not devices:
            if not isinstance(disks, list):
                disks = [disks]
            if not isinstance(volumes, list):
                volumes = [volumes]

            devices = []

            for d in disks:
                if d is None:
                    devices.append(None)
                elif isinstance(d, Disk):
                    devices.append(d)
                else:
                    devices.append(Disk(self._client, int(d), self.id))

            for v in volumes:
                if v is None:
                    devices.append(None)
                elif isinstance(v, Volume):
                    devices.append(v)
                else:
                    devices.append(Volume(self._client, int(v)))

        if not devices:
            raise ValueError('Must include at least one disk or volume!')

        for i, d in enumerate(devices):
            if d is None:
                pass
            elif isinstance(d, Disk):
                device_map[device_names[i]] = {'disk_id': d.id }
            elif isinstance(d, Volume):
                device_map[device_names[i]] = {'volume_id': d.id }
            else:
                raise TypeError('Disk or Volume expected!')

        params = {
            'kernel': kernel.id if issubclass(type(kernel), Base) else kernel,
            'label': label if label else "{}_config_{}".format(self.label, len(self.configs)),
            'devices': device_map,
        }
        params.update(kwargs)

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

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

        c = Config(self._client, result['id'], self.id, result)
        return c

    def create_disk(self, size, label=None, filesystem=None, read_only=False, image=None,
            root_pass=None, authorized_keys=None, stackscript=None, **stackscript_args):

        gen_pass = None
        if image and not root_pass:
            gen_pass  = Linode.generate_root_password()
            root_pass = gen_pass

        authorized_keys = load_and_validate_keys(authorized_keys)

        if image and not label:
            label = "My {} Disk".format(image.label)

        params = {
            'size': size,
            'label': label if label else "{}_disk_{}".format(self.label, len(self.disks)),
            'read_only': read_only,
            'filesystem': filesystem if filesystem else 'raw',
            'authorized_keys': authorized_keys,
        }

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

        if stackscript:
            params['stackscript_id'] = stackscript.id
            if stackscript_args:
                params['stackscript_data'] = stackscript_args

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

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

        d = Disk(self._client, result['id'], self.id, result)

        if gen_pass:
            return d, gen_pass
        return d

    def enable_backups(self):
        """
        Enable Backups for this Linode.  When enabled, we will automatically
        backup your Linode's data so that it can be restored at a later date.
        For more information on Linode's Backups service and pricing, see our
        `Backups Page`_

        .. _Backups Page: https://www.linode.com/backups
        """
        self._client.post("{}/backups/enable".format(Linode.api_endpoint), model=self)
        self.invalidate()
        return True

    def cancel_backups(self):
        """
        Cancels Backups for this Linode.  All existing Backups will be lost,
        including any snapshots that have been taken.  This cannot be undone,
        but Backups can be re-enabled at a later date.
        """
        self._client.post("{}/backups/cancel".format(Linode.api_endpoint), model=self)
        self.invalidate()
        return True

    def snapshot(self, label=None):
        result = self._client.post("{}/backups".format(Linode.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

    def allocate_ip(self, public=False):
        """
        Allocates a new :any:`IPAddress` for this Linode.  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
        Linode.

        :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(Linode.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 rebuild(self, image, root_pass=None, authorized_keys=None, **kwargs):
        """
        Rebuilding a Linode deletes all existing Disks and Configs and deploys
        a new :any:`Image` to it.  This can be used to reset an existing
        Linode or to install an Image on an empty Linode.

        :param image: The Image to deploy to this Linode
        :type image: str or Image
        :param root_pass: The root password for the newly rebuilt Linode.  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 = Linode.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(Linode.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

    def rescue(self, *disks):
        if disks:
            disks = { x: { 'disk_id': y } for x,y in zip(('sda','sdb','sdc','sdd','sde','sdf','sdg'), disks) }
        else:
            disks=None

        result = self._client.post('{}/rescue'.format(Linode.api_endpoint), model=self,
                data={ "devices": disks })

        return result

    def kvmify(self):
        """
        Converts this linode to KVM from Xen
        """
        self._client.post('{}/kvmify'.format(Linode.api_endpoint), model=self)

        return True

    def mutate(self):
        """
        Upgrades this Linode to the latest generation type
        """
        self._client.post('{}/mutate'.format(Linode.api_endpoint), model=self)

        return True

    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(Linode.api_endpoint), model=self, data=params)

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

        l = Linode(self._client, result['id'], result)
        return l

    @property
    def stats(self):
        """
        Returns the JSON stats for this Linode
        """
        # TODO - this would be nicer if we formatted the stats
        return self._client.get('{}/stats'.format(Linode.api_endpoint), model=self)

    def stats_for(self, dt):
        """
        Returns stats for the month containing the given datetime
        """
        # TODO - this would be nicer if we formatted the stats
        if not isinstance(dt, datetime):
            raise TypeError('stats_for requires a datetime object!')
        return self._client.get('{}/stats/'.format(dt.strftime('%Y/%m')))
Beispiel #28
0
class Image(Base):
    """
    An Image is something a Linode or Disk can be deployed from.
    """
    api_endpoint = '/images/{id}'

    properties = {
        "id": Property(identifier=True),
        "label": Property(mutable=True),
        "description": Property(mutable=True),
        "status": Property(),
        "created": Property(is_datetime=True),
        "created_by": Property(),
        "type": Property(),
        "is_public": Property(),
        "vendor": Property(),
        "size": Property(),
        "deprecated": Property()
    }
Beispiel #29
0
class Profile(Base):
    api_endpoint = "/profile"
    id_attribute = 'username'

    properties = {
        'username': Property(identifier=True),
        'uid': Property(),
        'email': Property(mutable=True),
        'timezone': Property(mutable=True),
        'email_notifications': Property(mutable=True),
        'referrals': Property(),
        'ip_whitelist_enabled': Property(mutable=True),
        'lish_auth_method': Property(mutable=True),
        'authorized_keys': Property(mutable=True),
        'two_factor_auth': Property(),
        'restricted': Property(),
    }

    def enable_tfa(self):
        """
        Enables TFA for the token's user.  This requies a follow-up request
        to confirm TFA.  Returns the TFA secret that needs to be confirmed.
        """
        result = self._client.post('/profile/tfa-enable')

        return result['secret']

    def confirm_tfa(self, code):
        """
        Confirms TFA for an account.  Needs a TFA code generated by enable_tfa
        """
        self._client.post('/profile/tfa-enable-confirm',
                          data={"tfa_code": code})

        return True

    def disable_tfa(self):
        """
        Turns off TFA for this user's account.
        """
        self._client.post('/profile/tfa-disable')

        return True

    @property
    def grants(self):
        """
        Returns grants for the current user
        """
        from linode.objects.account import UserGrants
        resp = self._client.get(
            '/profile/grants')  # use special endpoint for restricted users

        grants = None
        if resp is not None:
            # if resp is None, we're unrestricted and do not have grants
            grants = UserGrants(self._client, self.username, resp)

        return grants

    @property
    def whitelist(self):
        """
        Returns the user's whitelist entries, if whitelist is enabled
        """
        return self._client._get_and_filter(WhitelistEntry)

    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)
Beispiel #30
0
class NodeBalancerConfig(DerivedBase):
    api_endpoint = '/nodebalancers/{nodebalancer_id}/configs/{id}'
    derived_url_path = 'configs'
    parent_id_name = 'nodebalancer_id'

    properties = {
        'id': Property(identifier=True),
        'nodebalancer_id': Property(identifier=True),
        "port": Property(mutable=True),
        "protocol": Property(mutable=True),
        "algorithm": Property(mutable=True),
        "stickiness": Property(mutable=True),
        "check": Property(mutable=True),
        "check_interval": Property(mutable=True),
        "check_timeout": Property(mutable=True),
        "check_attempts": Property(mutable=True),
        "check_path": Property(mutable=True),
        "check_body": Property(mutable=True),
        "check_passive": Property(mutable=True),
        "ssl_cert": Property(mutable=True),
        "ssl_key": Property(mutable=True),
        "ssl_commonname": Property(),
        "ssl_fingerprint": Property(),
        "cipher_suite": Property(mutable=True),
        "nodes_status": Property(),
    }

    @property
    def nodes(self):
        """
        This is a special derived_class relationship because NodeBalancerNode is the
        only api object that requires two parent_ids
        """
        if not hasattr(self, '_nodes'):
            base_url = "{}/{}".format(NodeBalancerConfig.api_endpoint,
                                      NodeBalancerNode.derived_url_path)
            result = self._client._get_objects(
                base_url,
                NodeBalancerNode,
                model=self,
                parent_id=(self.id, self.nodebalancer_id))

            self._set('_nodes', result)

        return self._nodes

    def create_node(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

    def load_ssl_data(self, cert_file, key_file):
        """
        A convenience method that loads a cert and a key from files and sets them
        on this object.  This can make enabling ssl easier (instead of you needing
        to load the files yourself).

        This does *not* change protocol/port for you, or save anything.  Once this
        is called, you must still call `save()` on this object for the changes to
        take effect.

        :param cert_file: A path to the file containing the public certificate
        :type cert_file: str
        :param key_file: A path to the file containing the unpassphrased private key
        :type key_file: str
        """
        # we're disabling warnings here because these attributes are defined dynamically
        # through linode.objects.Base, and pylint isn't privy
        if os.path.isfile(os.path.expanduser(cert_file)):
            with open(os.path.expanduser(cert_file)) as f:
                self.ssl_cert = f.read()  # pylint: disable=attribute-defined-outside-init

        if os.path.isfile(os.path.expanduser(key_file)):
            with open(os.path.expanduser(key_file)) as f:
                self.ssl_key = f.read()  # pylint: disable=attribute-defined-outside-init