You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by je...@apache.org on 2011/01/04 05:28:40 UTC
svn commit: r1054893 - in /incubator/libcloud/trunk/libcloud: base.py
drivers/dummy.py
Author: jerry
Date: Tue Jan 4 04:28:40 2011
New Revision: 1054893
URL: http://svn.apache.org/viewvc?rev=1054893&view=rev
Log:
Documentation with doctests for base.py and dummy.py; LIBCLOUD-64
Submitted By: Michael De La Rue
Modified:
incubator/libcloud/trunk/libcloud/base.py
incubator/libcloud/trunk/libcloud/drivers/dummy.py
Modified: incubator/libcloud/trunk/libcloud/base.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/base.py?rev=1054893&r1=1054892&r2=1054893&view=diff
==============================================================================
--- incubator/libcloud/trunk/libcloud/base.py (original)
+++ incubator/libcloud/trunk/libcloud/base.py Tue Jan 4 04:28:40 2011
@@ -33,7 +33,48 @@ from pipes import quote as pquote
class Node(object):
"""
- A Base Node class to derive from.
+ Provide a common interface for handling nodes of all types.
+
+ The Node object provides the interface in libcloud through which
+ we can manipulate nodes in different cloud providers in the same
+ way. Node objects don't actually do much directly themselves,
+ instead the node driver handles the connection to the node.
+
+ You don't normally create a node object yourself; instead you use
+ a driver and then have that create the node for you.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> node.public_ip[0]
+ '127.0.0.3'
+ >>> node.name
+ 'dummy-3'
+
+ You can also get nodes from the driver's list_node function.
+
+ >>> node = driver.list_nodes()[0]
+ >>> node.name
+ 'dummy-1'
+
+ the node keeps a reference to its own driver which means that we
+ can work on nodes from different providers without having to know
+ which is which.
+
+ >>> driver = DummyNodeDriver(72)
+ >>> node2 = driver.create_node()
+ >>> node.driver.creds
+ 0
+ >>> node2.driver.creds
+ 72
+
+ Althrough Node objects can be subclassed, this isn't normally
+ done. Instead, any driver specific information is stored in the
+ "extra" proproperty of the node.
+
+ >>> node.extra
+ {'foo': 'bar'}
+
"""
def __init__(self, id, name, state, public_ip, private_ip,
@@ -52,19 +93,67 @@ class Node(object):
def get_uuid(self):
"""Unique hash for this node
+
@return: C{string}
+
+ The hash is a function of an SHA1 hash of the node's ID and
+ its driver which means that it should be unique between all
+ nodes. In some subclasses (e.g. GoGrid) there is no ID
+ available so the public IP address is used. This means that,
+ unlike a properly done system UUID, the same UUID may mean a
+ different system install at a different time
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> node.get_uuid()
+ 'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
+
+ Note, for example, that this example will always produce the
+ same UUID!
"""
return hashlib.sha1("%s:%d" % (self.id,self.driver.type)).hexdigest()
def reboot(self):
"""Reboot this node
+
@return: C{bool}
+
+ This calls the node's driver and reboots the node
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> from libcloud.types import NodeState
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> node.state == NodeState.REBOOTING
+ False
+ >>> node.reboot()
+ True
+ >>> node.state == NodeState.REBOOTING
+ True
"""
return self.driver.reboot_node(self)
def destroy(self):
"""Destroy this node
+
@return: C{bool}
+
+ This calls the node's driver and destroys the node
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> from libcloud.types import NodeState
+ >>> node = driver.create_node()
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> node.destroy()
+ True
+ >>> node.state == NodeState.RUNNING
+ False
+
"""
return self.driver.destroy_node(self)
@@ -78,6 +167,25 @@ class Node(object):
class NodeSize(object):
"""
A Base NodeSize class to derive from.
+
+ NodeSizes are objects which are typically returned a driver's
+ list_sizes function. They contain a number of different
+ parameters which define how big an image is.
+
+ The exact parameters available depends on the provider.
+
+ N.B. Where a parameter is "unlimited" (for example bandwidth in
+ Amazon) this will be given as 0.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> size = driver.list_sizes()[0]
+ >>> size.ram
+ 128
+ >>> size.bandwidth
+ 500
+ >>> size.price
+ 4
"""
def __init__(self, id, name, ram, disk, bandwidth, price, driver):
@@ -97,7 +205,25 @@ class NodeSize(object):
class NodeImage(object):
"""
- A Base NodeImage class to derive from.
+ An operating system image.
+
+ NodeImage objects are typically returned by the driver for the
+ cloud provider in response to the list_images function
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> image = driver.list_images()[0]
+ >>> image.name
+ 'Ubuntu 9.10'
+
+ Apart from name and id, there is no further standard information;
+ other parameters are stored in a driver specific "extra" variable
+
+ When creating a node, a node image should be given as an argument
+ to the create_node function to decide which OS image to use.
+
+ >>> node = driver.create_node(image=image)
+
"""
def __init__(self, id, name, driver, extra=None):
@@ -114,7 +240,13 @@ class NodeImage(object):
class NodeLocation(object):
"""
- A base NodeLocation class to derive from.
+ A physical location where nodes can be.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> location = driver.list_locations()[0]
+ >>> location.country
+ 'US'
"""
def __init__(self, id, name, country, driver):
@@ -129,6 +261,16 @@ class NodeLocation(object):
class NodeAuthSSHKey(object):
"""
An SSH key to be installed for authentication to a node.
+
+ This is the actual contents of the users ssh public key which will
+ normally be installed as root's public key on the node.
+
+ >>> pubkey = '...' # read from file
+ >>> from libcloud.base import NodeAuthSSHKey
+ >>> k = NodeAuthSSHKey(pubkey)
+ >>> k
+ <NodeAuthSSHKey>
+
"""
def __init__(self, pubkey):
self.pubkey = pubkey
@@ -494,6 +636,11 @@ class ConnectionUserAndKey(ConnectionKey
class NodeDriver(object):
"""
A base NodeDriver class to derive from
+
+ This class is always subclassed by a specific driver. For
+ examples of base behavior of most functions (except deploy node)
+ see the dummy driver.
+
"""
connectionCls = ConnectionKey
@@ -637,7 +784,7 @@ class NodeDriver(object):
Depends on a Provider Driver supporting either using a specific password
or returning a generated password.
- This function may raise a L{DeplyomentException}, if a create_node
+ This function may raise a L{DeploymentException}, if a create_node
call was successful, but there is a later error (like SSH failing or
timing out). This exception includes a Node object which you may want
to destroy if incomplete deployments are not desirable.
@@ -653,6 +800,24 @@ class NodeDriver(object):
@type ssh_port: C{int}
See L{NodeDriver.create_node} for more keyword args.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> from libcloud.deployment import ScriptDeployment, MultiStepDeployment
+ >>> from libcloud.base import NodeAuthSSHKey
+ >>> driver = DummyNodeDriver(0)
+ >>> key = NodeAuthSSHKey('...') # read from file
+ >>> script = ScriptDeployment("yum -y install emacs strace tcpdump")
+ >>> msd = MultiStepDeployment([key, script])
+ >>> def d():
+ ... try:
+ ... node = driver.deploy_node(deploy=msd)
+ ... except NotImplementedError:
+ ... print "not implemented for dummy driver"
+ >>> d()
+ not implemented for dummy driver
+
+ Deploy node is typically not overridden in subclasses. The
+ existing implementation should be able to handle most such.
"""
# TODO: support ssh keys
WAIT_PERIOD=3
@@ -738,3 +903,8 @@ def is_private_subnet(ip):
return True
return False
+
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
Modified: incubator/libcloud/trunk/libcloud/drivers/dummy.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/dummy.py?rev=1054893&r1=1054892&r2=1054893&view=diff
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/dummy.py (original)
+++ incubator/libcloud/trunk/libcloud/drivers/dummy.py Tue Jan 4 04:28:40 2011
@@ -36,6 +36,28 @@ class DummyConnection(ConnectionKey):
class DummyNodeDriver(NodeDriver):
"""
Dummy node driver
+
+ This is a fake driver which appears to always create or destroy
+ nodes successfully.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node=driver.create_node()
+ >>> node.public_ip[0]
+ '127.0.0.3'
+ >>> node.name
+ 'dummy-3'
+
+ If the credentials you give convert to an integer then the next
+ node to be created will be one higher.
+
+ Each time you create a node you will get a different IP address.
+
+ >>> driver = DummyNodeDriver(22)
+ >>> node=driver.create_node()
+ >>> node.name
+ 'dummy-23'
+
"""
name = "Dummy Node Provider"
@@ -84,18 +106,85 @@ class DummyNodeDriver(NodeDriver):
return str(uuid.uuid4())
def list_nodes(self):
+ """
+ List the nodes known to a particular driver;
+ There are two default nodes created at the beginning
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node_list=driver.list_nodes()
+ >>> sorted([node.name for node in node_list ])
+ ['dummy-1', 'dummy-2']
+
+ each item in the list returned is a node object from which you
+ can carry out any node actions you wish
+
+ >>> node_list[0].reboot()
+ True
+
+ As more nodes are added, list_nodes will return them
+
+ >>> node=driver.create_node()
+ >>> sorted([node.name for node in driver.list_nodes()])
+ ['dummy-1', 'dummy-2', 'dummy-3']
+ """
return self.nl
def reboot_node(self, node):
+ """
+ Sets the node state to rebooting; in this dummy driver always
+ returns True as if the reboot had been successful.
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node=driver.create_node()
+ >>> from libcloud.types import NodeState
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> node.state == NodeState.REBOOTING
+ False
+ >>> driver.reboot_node(node)
+ True
+ >>> node.state == NodeState.REBOOTING
+ True
+
+ Please note, dummy nodes never recover from the reboot.
+ """
+
node.state = NodeState.REBOOTING
return True
def destroy_node(self, node):
+ """
+ Sets the node state to terminated and removes it from the node list
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> from libcloud.types import NodeState
+ >>> node = [node for node in driver.list_nodes() if node.name == 'dummy-1'][0]
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> driver.destroy_node(node)
+ True
+ >>> node.state == NodeState.RUNNING
+ False
+ >>> [node for node in driver.list_nodes() if node.name == 'dummy-1']
+ []
+ """
+
node.state = NodeState.TERMINATED
self.nl.remove(node)
return True
def list_images(self, location=None):
+ """
+ Returns a list of images as a cloud provider might have
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> sorted([image.name for image in driver.list_images()])
+ ['Slackware 4', 'Ubuntu 9.04', 'Ubuntu 9.10']
+ """
return [
NodeImage(id=1, name="Ubuntu 9.10", driver=self),
NodeImage(id=2, name="Ubuntu 9.04", driver=self),
@@ -103,54 +192,89 @@ class DummyNodeDriver(NodeDriver):
]
def list_sizes(self, location=None):
+ """
+ Returns a list of node sizes as a cloud provider might have
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> sorted([size.ram for size in driver.list_sizes()])
+ [128, 512, 4096, 8192]
+ """
+
return [
- NodeSize(id=1,
- name="Small",
- ram=128,
- disk=4,
- bandwidth=500,
- price=4,
- driver=self),
- NodeSize(id=2,
- name="Medium",
- ram=512,
- disk=16,
- bandwidth=1500,
- price=8,
- driver=self),
- NodeSize(id=3,
- name="Big",
- ram=4096,
- disk=32,
- bandwidth=2500,
- price=32,
- driver=self),
- NodeSize(id=4,
- name="XXL Big",
- ram=4096*2,
- disk=32*4,
- bandwidth=2500*3,
- price=32*2,
- driver=self),
+ NodeSize(id=1,
+ name="Small",
+ ram=128,
+ disk=4,
+ bandwidth=500,
+ price=4,
+ driver=self),
+ NodeSize(id=2,
+ name="Medium",
+ ram=512,
+ disk=16,
+ bandwidth=1500,
+ price=8,
+ driver=self),
+ NodeSize(id=3,
+ name="Big",
+ ram=4096,
+ disk=32,
+ bandwidth=2500,
+ price=32,
+ driver=self),
+ NodeSize(id=4,
+ name="XXL Big",
+ ram=4096*2,
+ disk=32*4,
+ bandwidth=2500*3,
+ price=32*2,
+ driver=self),
]
def list_locations(self):
+ """
+ Returns a list of locations of nodes
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> sorted([loc.name + " in " + loc.country for loc in driver.list_locations()])
+ ['Island Datacenter in FJ', 'London Loft in GB', "Paul's Room in US"]
+ """
return [
- NodeLocation(id=1,
- name="Paul's Room",
- country='US',
- driver=self),
- NodeLocation(id=2,
- name="London Loft",
- country='GB',
- driver=self),
- NodeLocation(id=3,
- name="Island Datacenter",
- country='FJ',
- driver=self),
+ NodeLocation(id=1,
+ name="Paul's Room",
+ country='US',
+ driver=self),
+ NodeLocation(id=2,
+ name="London Loft",
+ country='GB',
+ driver=self),
+ NodeLocation(id=3,
+ name="Island Datacenter",
+ country='FJ',
+ driver=self),
]
def create_node(self, **kwargs):
+ """
+ Creates a dummy node; the node id is equal to the number of
+ nodes in the node list
+
+ >>> from libcloud.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> sorted([node.name for node in driver.list_nodes()])
+ ['dummy-1', 'dummy-2']
+ >>> nodeA = driver.create_node()
+ >>> sorted([node.name for node in driver.list_nodes()])
+ ['dummy-1', 'dummy-2', 'dummy-3']
+ >>> driver.create_node().name
+ 'dummy-4'
+ >>> driver.destroy_node(nodeA)
+ True
+ >>> sorted([node.name for node in driver.list_nodes()])
+ ['dummy-1', 'dummy-2', 'dummy-4']
+ """
l = len(self.nl) + 1
n = Node(id=l,
name='dummy-%d' % l,
@@ -167,3 +291,7 @@ def _ip_to_int(ip):
def _int_to_ip(ip):
return socket.inet_ntoa(struct.pack('I', socket.ntohl(ip)))
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()