Mini Shell
# 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.
"""
libcloud driver for the Blue Box Blocks API
This driver implements all libcloud functionality for the Blue Box Blocks API.
Blue Box home page http://bluebox.net
Blue Box API documentation https://boxpanel.bluebox
.net/public/the_vault/index.php/Blocks_API
"""
import copy
import base64
from libcloud.utils.py3 import urlencode
from libcloud.utils.py3 import b
from libcloud.common.base import JsonResponse, ConnectionUserAndKey
from libcloud.compute.providers import Provider
from libcloud.compute.types import NodeState, InvalidCredsError
from libcloud.compute.base import Node, NodeDriver
from libcloud.compute.base import NodeSize, NodeImage, NodeLocation
from libcloud.compute.base import NodeAuthPassword, NodeAuthSSHKey
# Current end point for Blue Box API.
BLUEBOX_API_HOST = "boxpanel.bluebox.net"
# The API doesn't currently expose all of the required values for libcloud,
# so we simply list what's available right now, along with all of the various
# attributes that are needed by libcloud.
BLUEBOX_INSTANCE_TYPES = {
'1gb': {
'id': '94fd37a7-2606-47f7-84d5-9000deda52ae',
'name': 'Block 1GB Virtual Server',
'ram': 1024,
'disk': 20,
'cpu': 0.5
},
'2gb': {
'id': 'b412f354-5056-4bf0-a42f-6ddd998aa092',
'name': 'Block 2GB Virtual Server',
'ram': 2048,
'disk': 25,
'cpu': 1
},
'4gb': {
'id': '0cd183d3-0287-4b1a-8288-b3ea8302ed58',
'name': 'Block 4GB Virtual Server',
'ram': 4096,
'disk': 50,
'cpu': 2
},
'8gb': {
'id': 'b9b87a5b-2885-4a2e-b434-44a163ca6251',
'name': 'Block 8GB Virtual Server',
'ram': 8192,
'disk': 100,
'cpu': 4
}
}
RAM_PER_CPU = 2048
NODE_STATE_MAP = {'queued': NodeState.PENDING,
'building': NodeState.PENDING,
'running': NodeState.RUNNING,
'error': NodeState.TERMINATED,
'unknown': NodeState.UNKNOWN}
class BlueboxResponse(JsonResponse):
def parse_error(self):
if int(self.status) == 401:
if not self.body:
raise InvalidCredsError(str(self.status) + ': ' + self.error)
else:
raise InvalidCredsError(self.body)
return self.body
class BlueboxNodeSize(NodeSize):
def __init__(self, id, name, cpu, ram, disk, price, driver):
self.id = id
self.name = name
self.cpu = cpu
self.ram = ram
self.disk = disk
self.price = price
self.driver = driver
def __repr__(self):
return ((
'<NodeSize: id=%s, name=%s, cpu=%s, ram=%s, disk=%s, '
'price=%s, driver=%s ...>')
% (self.id, self.name, self.cpu, self.ram, self.disk,
self.price, self.driver.name))
class BlueboxConnection(ConnectionUserAndKey):
"""
Connection class for the Bluebox driver
"""
host = BLUEBOX_API_HOST
secure = True
responseCls = BlueboxResponse
allow_insecure = False
def add_default_headers(self, headers):
user_b64 = base64.b64encode(b('%s:%s' % (self.user_id, self.key)))
headers['Authorization'] = 'Basic %s' % (user_b64)
return headers
class BlueboxNodeDriver(NodeDriver):
"""
Bluebox Blocks node driver
"""
connectionCls = BlueboxConnection
type = Provider.BLUEBOX
api_name = 'bluebox'
name = 'Bluebox Blocks'
website = 'http://bluebox.net'
features = {'create_node': ['ssh_key', 'password']}
def list_nodes(self):
result = self.connection.request('/api/blocks.json')
return [self._to_node(i) for i in result.object]
def list_sizes(self, location=None):
sizes = []
for key, values in list(BLUEBOX_INSTANCE_TYPES.items()):
attributes = copy.deepcopy(values)
attributes.update({'price': self._get_size_price(size_id=key)})
sizes.append(BlueboxNodeSize(driver=self.connection.driver,
**attributes))
return sizes
def list_images(self, location=None):
result = self.connection.request('/api/block_templates.json')
images = []
for image in result.object:
images.extend([self._to_image(image)])
return images
def create_node(self, name, size, image, auth=None, ex_username=None):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
auth = self._get_and_check_auth(auth)
data = {
'hostname': name,
'product': size.id,
'template': image.id
}
ssh = None
password = None
if isinstance(auth, NodeAuthSSHKey):
ssh = auth.pubkey # pylint: disable=no-member
data.update(ssh_public_key=ssh)
elif isinstance(auth, NodeAuthPassword):
password = auth.password
data.update(password=password)
if ex_username:
data.update(username=ex_username)
if not ssh and not password:
raise Exception("SSH public key or password required.")
params = urlencode(data)
result = self.connection.request('/api/blocks.json', headers=headers,
data=params, method='POST')
node = self._to_node(result.object)
if getattr(auth, "generated", False):
node.extra['password'] = auth.password
return node
def destroy_node(self, node):
url = '/api/blocks/%s.json' % (node.id)
result = self.connection.request(url, method='DELETE')
return result.status == 200
def list_locations(self):
return [NodeLocation(0, "Blue Box Seattle US", 'US', self)]
def reboot_node(self, node):
url = '/api/blocks/%s/reboot.json' % (node.id)
result = self.connection.request(url, method="PUT")
return result.status == 200
def _to_node(self, vm):
state = NODE_STATE_MAP[vm.get('status', NodeState.UNKNOWN)]
n = Node(id=vm['id'],
name=vm['hostname'],
state=state,
public_ips=[ip['address'] for ip in vm['ips']],
private_ips=[],
extra={'storage': vm['storage'], 'cpu': vm['cpu']},
driver=self.connection.driver)
return n
def _to_image(self, image):
image = NodeImage(id=image['id'],
name=image['description'],
driver=self.connection.driver)
return image
Zerion Mini Shell 1.0