Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/_static/images/provider_logos/packet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/compute/_supported_methods_block_storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Provider list volumes create volume destroy volume
`Opsource`_ no no no no no no no
`Outscale INC`_ yes yes yes yes yes yes yes
`Outscale SAS`_ yes yes yes yes yes yes yes
`Packet`_ no no no no no no no
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for a future reference - in case you updated those files manually, they are generated automatically using ./contrib/generate_provider_feature_matrix_table.py script.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, gtk thanks

`ProfitBricks`_ yes yes yes yes yes no no
`Rackspace Cloud (Next Gen)`_ yes yes yes yes yes yes yes
`Rackspace Cloud (First Gen)`_ yes yes yes yes yes no no
Expand Down Expand Up @@ -125,6 +126,7 @@ Provider list volumes create volume destroy volume
.. _`Opsource`: http://www.opsource.net/
.. _`Outscale INC`: http://www.outscale.com
.. _`Outscale SAS`: http://www.outscale.com
.. _`Packet`: http://www.packet.net/
.. _`ProfitBricks`: http://www.profitbricks.com
.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com
.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com
Expand Down
2 changes: 2 additions & 0 deletions docs/compute/_supported_methods_image_management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Provider list images get image create image delete
`Opsource`_ yes no no no no
`Outscale INC`_ yes yes yes yes yes
`Outscale SAS`_ yes yes yes yes yes
`Packet`_ yes no no no no
`ProfitBricks`_ yes no no no no
`Rackspace Cloud (Next Gen)`_ yes yes yes yes no
`Rackspace Cloud (First Gen)`_ yes yes yes yes no
Expand Down Expand Up @@ -125,6 +126,7 @@ Provider list images get image create image delete
.. _`Opsource`: http://www.opsource.net/
.. _`Outscale INC`: http://www.outscale.com
.. _`Outscale SAS`: http://www.outscale.com
.. _`Packet`: http://www.packet.net/
.. _`ProfitBricks`: http://www.profitbricks.com
.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com
.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com
Expand Down
2 changes: 2 additions & 0 deletions docs/compute/_supported_methods_key_pair_management.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Provider list key pairs get key pair create key pai
`Opsource`_ no no no no no no
`Outscale INC`_ yes yes yes yes no yes
`Outscale SAS`_ yes yes yes yes no yes
`Packet`_ yes no yes no no yes
`ProfitBricks`_ no no no no no no
`Rackspace Cloud (Next Gen)`_ yes yes yes yes no yes
`Rackspace Cloud (First Gen)`_ no no no no no no
Expand Down Expand Up @@ -125,6 +126,7 @@ Provider list key pairs get key pair create key pai
.. _`Opsource`: http://www.opsource.net/
.. _`Outscale INC`: http://www.outscale.com
.. _`Outscale SAS`: http://www.outscale.com
.. _`Packet`: http://www.packet.net/
.. _`ProfitBricks`: http://www.profitbricks.com
.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com
.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com
Expand Down
2 changes: 2 additions & 0 deletions docs/compute/_supported_methods_main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Provider list nodes create node reboot node destroy
`Opsource`_ yes yes yes yes yes yes yes
`Outscale INC`_ yes yes yes yes yes yes yes
`Outscale SAS`_ yes yes yes yes yes yes yes
`Packet`_ yes yes yes yes yes yes no
`ProfitBricks`_ yes yes yes yes yes yes no
`Rackspace Cloud (Next Gen)`_ yes yes yes yes yes yes yes
`Rackspace Cloud (First Gen)`_ yes yes yes yes yes yes yes
Expand Down Expand Up @@ -125,6 +126,7 @@ Provider list nodes create node reboot node destroy
.. _`Opsource`: http://www.opsource.net/
.. _`Outscale INC`: http://www.outscale.com
.. _`Outscale SAS`: http://www.outscale.com
.. _`Packet`: http://www.packet.net/
.. _`ProfitBricks`: http://www.profitbricks.com
.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com
.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com
Expand Down
2 changes: 2 additions & 0 deletions docs/compute/_supported_providers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Provider Documentation
`Opsource`_ OPSOURCE :mod:`libcloud.compute.drivers.opsource` :class:`OpsourceNodeDriver`
`Outscale INC`_ :doc:`Click </compute/drivers/outscale_inc>` OUTSCALE_INC :mod:`libcloud.compute.drivers.ec2` :class:`OutscaleINCNodeDriver`
`Outscale SAS`_ :doc:`Click </compute/drivers/outscale_sas>` OUTSCALE_SAS :mod:`libcloud.compute.drivers.ec2` :class:`OutscaleSASNodeDriver`
`Packet`_ :doc:`Click </compute/drivers/packet>` PACKET :mod:`libcloud.compute.drivers.packet` :class:`PacketNodeDriver`
`ProfitBricks`_ PROFIT_BRICKS :mod:`libcloud.compute.drivers.profitbricks` :class:`ProfitBricksNodeDriver`
`Rackspace Cloud (Next Gen)`_ :doc:`Click </compute/drivers/rackspace>` RACKSPACE :mod:`libcloud.compute.drivers.rackspace` :class:`RackspaceNodeDriver`
`Rackspace Cloud (First Gen)`_ RACKSPACE_FIRST_GEN :mod:`libcloud.compute.drivers.rackspace` :class:`RackspaceFirstGenNodeDriver`
Expand Down Expand Up @@ -125,6 +126,7 @@ Provider Documentation
.. _`Opsource`: http://www.opsource.net/
.. _`Outscale INC`: http://www.outscale.com
.. _`Outscale SAS`: http://www.outscale.com
.. _`Packet`: http://www.packet.net/
.. _`ProfitBricks`: http://www.profitbricks.com
.. _`Rackspace Cloud (Next Gen)`: http://www.rackspace.com
.. _`Rackspace Cloud (First Gen)`: http://www.rackspace.com
Expand Down
25 changes: 25 additions & 0 deletions docs/compute/drivers/packet.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Packet Compute Driver Documentation
=========================================

`Packet`_ is a dedicated bare metal cloud hosting provider based in New York
City

.. figure:: /_static/images/provider_logos/packet.png
:align: center
:width: 300
:target: https://www.packet.net/

Instantiating a driver and listing devices in a project
-------------------------------------------------------

.. literalinclude:: /examples/compute/packet/instantiate_api_v1.0.py
:language: python

API Docs
--------

.. autoclass:: libcloud.compute.drivers.packet.Packet_v1_NodeDriver
:members:
:inherited-members:

.. _`Packet`: https://www.packet.net/
10 changes: 10 additions & 0 deletions docs/examples/compute/packet/instantiate_api_v1.0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

cls = get_driver(Provider.PACKET)

driver = cls('your API auth token')

nodes = driver.list_nodes('project-id')
for node in nodes:
print(node)
256 changes: 256 additions & 0 deletions libcloud/compute/drivers/packet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Packet Driver
"""

from libcloud.utils.py3 import httplib

from libcloud.common.base import ConnectionKey, JsonResponse
from libcloud.compute.types import Provider, NodeState, InvalidCredsError
from libcloud.compute.base import NodeDriver, Node
from libcloud.compute.base import NodeImage, NodeSize, NodeLocation
from libcloud.compute.base import KeyPair

PACKET_ENDPOINT = "api.packet.net"


class PacketResponse(JsonResponse):
valid_response_codes = [httplib.OK, httplib.ACCEPTED, httplib.CREATED,
httplib.NO_CONTENT]

def parse_error(self):
if self.status == httplib.UNAUTHORIZED:
body = self.parse_body()
raise InvalidCredsError(body['message'])
else:
body = self.parse_body()
if 'message' in body:
error = '%s (code: %s)' % (body['message'], self.status)
else:
error = body
return error

def success(self):
return self.status in self.valid_response_codes


class PacketConnection(ConnectionKey):
"""
Connection class for the Packet driver.
"""

host = PACKET_ENDPOINT
responseCls = PacketResponse

def add_default_headers(self, headers):
"""
Add headers that are necessary for every request
"""
headers['Content-Type'] = 'application/json'
headers['X-Auth-Token'] = self.key
headers['X-Consumer-Token'] = \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use this is a testing token and should be self.key? :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh, fair enough, will fix

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually meant the X-Consumer-Token. Or is this some kind of static token which is shared among all users?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, exactly, we issue a consumer token for any platform or library that is integrated with us so we can tell what application requests are originating from. If calls are coming in with only an auth key, they will work, but be rate limited. This token in particular is the one we issued for libcloud. It doesn't need to be secured, people could technically abuse it or remove it if they wanted to, but then we'd rely on the auth token to relate those requests to a particular account and deal with it at that level. We have one for our mobile apps, docker machine, rancher, devo.ps, etc...

Does that make sense? Do you see an issue with it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, that makes sense. Thanks for the clarification.

P.S. We also try to be a good citizen and indicate that the request is made by Libcloud by sending a right user-agent header (e.g. User-Agent: libcloud/0.17.1-dev (Rackspace Cloud (Next Gen)) :)).

'kcrhMn7hwG8Ceo2hAhGFa2qpxLBvVHxEjS9ue8iqmsNkeeB2iQgMq4dNc1893pYu'
return headers


class PacketNodeDriver(NodeDriver):
"""
Packet NodeDriver
"""

connectionCls = PacketConnection
type = Provider.PACKET
name = 'Packet'
website = 'http://www.packet.net/'

NODE_STATE_MAP = {'queued': NodeState.PENDING,
'provisioning': NodeState.PENDING,
'rebuilding': NodeState.PENDING,
'powering_on': NodeState.REBOOTING,
'powering_off': NodeState.REBOOTING,
'rebooting': NodeState.REBOOTING,
'inactive': NodeState.STOPPED,
'deleted': NodeState.TERMINATED,
'deprovisioning': NodeState.TERMINATED,
'failed': NodeState.ERROR,
'active': NodeState.RUNNING}

def list_nodes(self, projectid):
data = self.connection.request('/projects/%s/devices' % (projectid),
params={'include': 'plan'}
).object['devices']
return list(map(self._to_node, data))

def list_locations(self):
data = self.connection.request('/facilities')\
.object['facilities']
return list(map(self._to_location, data))

def list_images(self):
data = self.connection.request('/operating-systems')\
.object['operating_systems']
return list(map(self._to_image, data))

def list_sizes(self):
data = self.connection.request('/plans').object['plans']
return list(map(self._to_size, data))

def create_node(self, project_id, name, size, image, location):
"""
Create a node.

:return: The newly created node.
:rtype: :class:`Node`
"""

params = {'hostname': name, 'plan': size.id,
'operating_system': image.id, 'facility': location.id,
'include': 'plan', 'billing_cycle': 'hourly'}

data = self.connection.request('/projects/%s/devices' % (project_id),
params=params, method='POST')

status = data.object.get('status', 'OK')
if status == 'ERROR':
message = data.object.get('message', None)
error_message = data.object.get('error_message', message)
raise ValueError('Failed to create node: %s' % (error_message))
return self._to_node(data=data.object)

def reboot_node(self, node):
params = {'type': 'reboot'}
res = self.connection.request('/devices/%s/actions' % (node.id),
params=params, method='POST')
return res.status == httplib.OK

def destroy_node(self, node):
res = self.connection.request('/devices/%s' % (node.id),
method='DELETE')
return res.status == httplib.OK

def list_key_pairs(self):
"""
List all the available SSH keys.

:return: Available SSH keys.
:rtype: ``list`` of :class:`.KeyPair` objects
"""
data = self.connection.request('/ssh-keys').object['ssh_keys']
return list(map(self._to_key_pairs, data))

def create_key_pair(self, name, public_key):
"""
Create a new SSH key.

:param name: Key name (required)
:type name: ``str``

:param public_key: Valid public key string (required)
:type public_key: ``str``
"""
params = {'label': name, 'key': public_key}
data = self.connection.request('/ssh-keys', method='POST',
params=params).object
return self._to_key_pairs(data)

def delete_key_pair(self, key):
"""
Delete an existing SSH key.

:param key: SSH key (required)
:type key: :class:`KeyPair`
"""
key_id = key.name
res = self.connection.request('/ssh-keys/%s' % (key_id),
method='DELETE')
return res.status == httplib.NO_CONTENT

def _to_node(self, data):
extra_keys = ['created_at', 'updated_at',
'userdata', 'billing_cycle', 'locked']
if 'state' in data:
state = self.NODE_STATE_MAP.get(data['state'], NodeState.UNKNOWN)
else:
state = NodeState.UNKNOWN

if 'ip_addresses' in data and data['ip_addresses'] is not None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be made a bit more readable if you refactor it in a new method - e.g. _get_ips_for_node or similar.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A+, will do!

ips = self._parse_ips(data['ip_addresses'])

if 'operating_system' in data and data['operating_system'] is not None:
image = self._to_image(data['operating_system'])

if 'plan' in data and data['plan'] is not None:
size = self._to_size(data['plan'])

extra = {}
for key in extra_keys:
if key in data:
extra[key] = data[key]

node = Node(id=data['id'], name=data['hostname'], state=state,
image=image, size=size,
public_ips=ips['public'], private_ips=ips['private'],
extra=extra, driver=self)
return node

def _to_image(self, data):
extra = {'distro': data['distro'], 'version': data['version']}
return NodeImage(id=data['slug'], name=data['name'], extra=extra,
driver=self)

def _to_location(self, data):
return NodeLocation(id=data['code'], name=data['name'], country=None,
driver=self)

def _to_size(self, data):
extra = {'description': data['description'], 'line': data['line']}

ram = data['specs']['memory']['total'].lower()
if 'mb' in ram:
ram = int(ram.replace('mb', ''))
elif 'gb' in ram:
ram = int(ram.replace('gb', '')) * 1024

disk = 0
for disks in data['specs']['drives']:
disk += disks['count'] * int(disks['size'].replace('GB', ''))

price = data['pricing']['hourly']

return NodeSize(id=data['slug'], name=data['name'], ram=ram, disk=disk,
bandwidth=0, price=price, extra=extra, driver=self)

def _to_key_pairs(self, data):
extra = {'label': data['label'],
'created_at': data['created_at'],
'updated_at': data['updated_at']}
return KeyPair(name=data['id'],
fingerprint=data['fingerprint'],
public_key=data['key'],
private_key=None,
driver=self,
extra=extra)

def _parse_ips(self, data):
public_ips = []
private_ips = []
for address in data:
if 'address' in address and address['address'] is not None:
if 'public' in address and address['public'] is True:
public_ips.append(address['address'])
else:
private_ips.append(address['address'])
return {'public': public_ips, 'private': private_ips}
2 changes: 2 additions & 0 deletions libcloud/compute/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@
('libcloud.compute.drivers.auroracompute', 'AuroraComputeNodeDriver'),
Provider.CLOUDWATT:
('libcloud.compute.drivers.cloudwatt', 'CloudwattNodeDriver'),
Provider.PACKET:
('libcloud.compute.drivers.packet', 'PacketNodeDriver'),

# Deprecated
Provider.CLOUDSIGMA_US:
Expand Down
1 change: 1 addition & 0 deletions libcloud/compute/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class Provider(object):
VULTR = 'vultr'
AURORACOMPUTE = 'aurora_compute'
CLOUDWATT = 'cloudwatt'
PACKET = 'packet'

# OpenStack based providers
HPCLOUD = 'hpcloud'
Expand Down
Loading