From 2b7ab77830c7371e1d0a7458eacb2254fa55f003 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Fri, 26 Feb 2010 18:53:49 -0800
Subject: [PATCH] Added placehold dreamhost driver and unit tests.

---
 libcloud/drivers/dreamhost.py |   33 ++++++++++++++++++++++++
 test/test_dreamhost.py        |   55 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+), 0 deletions(-)
 create mode 100644 libcloud/drivers/dreamhost.py
 create mode 100644 test/test_dreamhost.py

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
new file mode 100644
index 0000000..d731f2c
--- /dev/null
+++ b/libcloud/drivers/dreamhost.py
@@ -0,0 +1,33 @@
+# 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.
+# libcloud.org 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.
+"""
+DreamHost Driver
+"""
+from libcloud.interface import INodeDriver
+from libcloud.base import ConnectionKey, Response, NodeDriver, Node
+from libcloud.base import NodeSize, NodeImage, NodeLocation
+from libcloud.types import Provider, NodeState
+from zope.interface import implements
+
+class DreamhostResponse(Response):
+	raise NotImplementedError
+
+class DreamhostConnection(ConnectionKey):
+	raise NotImplementedError
+
+class DreamhostNodeDriver(NodeDriver):
+	raise NotImplementedError
+
+
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
new file mode 100644
index 0000000..354d7d0
--- /dev/null
+++ b/test/test_dreamhost.py
@@ -0,0 +1,55 @@
+# 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.
+# libcloud.org 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.
+import sys
+import unittest
+
+from libcloud.drivers.dreamhost import DreamhostNodeDriver as Dreamhost
+from libcloud.types import Provider, NodeState
+from libcloud.base import Node, NodeImage, NodeSize
+
+import httplib
+
+from test import MockHttp, multipleresponse, TestCaseMixin
+from secrets import DREAMHOST_KEY
+
+class DreamhostTest(unittest.TestCase, TestCaseMixin):
+
+	def setUp(self):
+		raise NotImplementedError
+
+	def test_list_nodes(self):
+		raise NotImplementedError
+
+	def test_list_images(self):
+		raise NotImplementedError
+	
+	def test_create_node(self):
+		raise NotImplementedError
+
+	def test_destroy_node(self):
+		raise NotImplementedError
+
+	def test_reboot_node(self):
+		raise NotImplementedError
+
+	def test_list_sizes(self):
+		raise NotImplementedError
+
+	def test_list_locations(self):
+		raise NotImplementedError
+	
+if __name__ == '__main__':
+	sys.exit(unittest.main())
+
-- 
1.5.4.1


From eae6fd7af9072d200bd3420073c126429e14fe5d Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Fri, 26 Feb 2010 20:25:58 -0800
Subject: [PATCH] DreamHost PS driver populated with placeholder methods.
 DreamHost PS unit test setUp completes successfully. (TestCaseMixin tests fail)

---
 libcloud/drivers/dreamhost.py |   50 +++++++++++++++++++++++++++++++++++++---
 test/test_dreamhost.py        |   41 ++++++++++++++++++++-------------
 2 files changed, 71 insertions(+), 20 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index d731f2c..0e0b156 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -19,15 +19,57 @@ from libcloud.interface import INodeDriver
 from libcloud.base import ConnectionKey, Response, NodeDriver, Node
 from libcloud.base import NodeSize, NodeImage, NodeLocation
 from libcloud.types import Provider, NodeState
-from zope.interface import implements
+#from zope.interface import implements
+import uuid
 
 class DreamhostResponse(Response):
-	raise NotImplementedError
+	"""
+	Response class for DreamHost PS
+	"""
+
+	def parse_body(self):
+		raise NotImplementedError
+
+	def parse_error(self):
+		raise NotImplementedError
 
 class DreamhostConnection(ConnectionKey):
-	raise NotImplementedError
+	"""
+	Connection class to connect to DreamHost's API servers
+	"""
+
+	host = 'api.dreamhost.com'
+	responseCls = DreamhostResponse
+
+	def add_default_params(self, params):
+		#self.key
+		raise NotImplementedError
+
 
 class DreamhostNodeDriver(NodeDriver):
-	raise NotImplementedError
+	"""
+	Node Driver for DreamHost PS
+	"""
+	
+	def create_node(self, **kwargs):
+		raise NotImplementedError
+	
+	def destroy_node(self, **kwargs):
+		raise NotImplementedError
+	
+	def reboot_node(self, **kwargs):
+		raise NotImplementedError
+	
+	def list_nodes(self, **kwargs):
+		raise NotImplementedError
+	
+	def list_images(self, **kwargs):
+		raise NotImplementedError
+
+	def list_sizes(self, **kwargs):
+		raise NotImplementedError
+
+	def list_locations(self, **kwargs):
+		raise NotImplementedError
 
 
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 354d7d0..3f372be 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -15,7 +15,7 @@
 import sys
 import unittest
 
-from libcloud.drivers.dreamhost import DreamhostNodeDriver as Dreamhost
+from libcloud.drivers.dreamhost import DreamhostNodeDriver
 from libcloud.types import Provider, NodeState
 from libcloud.base import Node, NodeImage, NodeSize
 
@@ -27,29 +27,38 @@ from secrets import DREAMHOST_KEY
 class DreamhostTest(unittest.TestCase, TestCaseMixin):
 
 	def setUp(self):
-		raise NotImplementedError
+		DreamhostNodeDriver.connectionCls.conn_classes = (
+			None,
+			DreamhostMockHttp
+		)
+		DreamhostMockHttp.use_param = 'cmd'
+		self.driver = DreamhostNodeDriver('foo')
 
-	def test_list_nodes(self):
-		raise NotImplementedError
+	#def test_list_nodes(self):
+		#raise NotImplementedError
 
-	def test_list_images(self):
-		raise NotImplementedError
+	#def test_list_images(self):
+		#raise NotImplementedError
 	
-	def test_create_node(self):
-		raise NotImplementedError
+	#def test_create_node(self):
+		#raise NotImplementedError
 
-	def test_destroy_node(self):
-		raise NotImplementedError
+	#def test_destroy_node(self):
+		#raise NotImplementedError
 
-	def test_reboot_node(self):
-		raise NotImplementedError
+	#def test_reboot_node(self):
+		#raise NotImplementedError
 
-	def test_list_sizes(self):
-		raise NotImplementedError
+	#def test_list_sizes(self):
+		#raise NotImplementedError
 
-	def test_list_locations(self):
-		raise NotImplementedError
+	#def test_list_locations(self):
+		#raise NotImplementedError
 	
+class DreamhostMockHttp(MockHttp):
+	#methods should be: _command_name:
+	pass
+
 if __name__ == '__main__':
 	sys.exit(unittest.main())
 
-- 
1.5.4.1


From 1d74a56f9f55529b43ad27b81e5ba5f01adff2a6 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Sun, 28 Feb 2010 23:10:40 -0800
Subject: [PATCH] Wrote unit test for list_nodes (successful)

---
 test/test_dreamhost.py |   61 +++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 3f372be..59ed32f 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -24,7 +24,8 @@ import httplib
 from test import MockHttp, multipleresponse, TestCaseMixin
 from secrets import DREAMHOST_KEY
 
-class DreamhostTest(unittest.TestCase, TestCaseMixin):
+#class DreamhostTest(unittest.TestCase, TestCaseMixin):
+class DreamhostTest(unittest.TestCase):
 
 	def setUp(self):
 		DreamhostNodeDriver.connectionCls.conn_classes = (
@@ -34,8 +35,62 @@ class DreamhostTest(unittest.TestCase, TestCaseMixin):
 		DreamhostMockHttp.use_param = 'cmd'
 		self.driver = DreamhostNodeDriver('foo')
 
-	#def test_list_nodes(self):
-		#raise NotImplementedError
+	def test_list_nodes(self):
+		"""
+		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
+			-	account_id: 000000
+				ip: 75.119.203.51
+				memory_mb: 500
+				ps: ps22174
+				start_date: 2010-02-25
+				type: web
+			-	account_id: 000000
+				ip: 75.119.203.52
+				memory_mb: 1500
+				ps: ps22175
+				start_date: 2010-02-25
+				type: mysql
+		"""
+
+		nodes = self.driver.list_nodes()
+		self.assertEqual(len(nodes), 2)
+		web_node = nodes[0]
+		mysql_node = nodes[1]
+		# Web node tests
+		self.assertEqual(web_node.id, ps22174)
+		#self.assertEqual(web_node.name, ps22174)
+		self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		self.assertTrue('75.119.203.51' in web_node.public_ip)
+		self.assertTrue(
+			web_node.extra.has_key('current_size') and
+			web_node.extra['current_size'] == 500
+		)
+		self.assertTrue(
+			web_node.extra.has_key('account_id') and
+			web_node.extra['account_id'] == '000000'
+		)
+		self.assertTrue(
+			web_node.extra.has_key('type') and
+			web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
+		)
+		# MySql node tests
+		self.assertEqual(web_node.id, ps22175)
+		#self.assertEqual(web_node.name, ps22175)
+		self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		self.assertTrue('75.119.203.52' in web_node.public_ip)
+		self.assertTrue(
+			web_node.extra.has_key('current_size') and
+			web_node.extra['current_size'] == 1500
+		)
+		self.assertTrue(
+			web_node.extra.has_key('account_id') and
+			web_node.extra['account_id'] == '000000'
+		)
+		self.assertTrue(
+			web_node.extra.has_key('type') and
+			web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
+		)
+
 
 	#def test_list_images(self):
 		#raise NotImplementedError
-- 
1.5.4.1


From ccd871e1e2a78427d23d672ce3038a2d4447d1ec Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Tue, 2 Mar 2010 14:56:44 -0800
Subject: [PATCH] Unit Test: dreamhost list_images succeeds

-Wrote unit test for dreamhost ps list_images.
-Implemented dirvers/dreamhost.py's list_images method.
---
 libcloud/drivers/dreamhost.py |   20 ++++---
 test/test_dreamhost.py        |  120 ++++++++++++++++++++++-------------------
 2 files changed, 77 insertions(+), 63 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 0e0b156..a2ed686 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -50,26 +50,30 @@ class DreamhostNodeDriver(NodeDriver):
 	"""
 	Node Driver for DreamHost PS
 	"""
-	
+
 	def create_node(self, **kwargs):
 		raise NotImplementedError
-	
+
 	def destroy_node(self, **kwargs):
 		raise NotImplementedError
-	
+
 	def reboot_node(self, **kwargs):
 		raise NotImplementedError
-	
+
 	def list_nodes(self, **kwargs):
 		raise NotImplementedError
-	
+
 	def list_images(self, **kwargs):
-		raise NotImplementedError
+		webimage = NodeImage(id='dh_ps_web', name='web', driver=self.connection.driver)
+		mysqlimage = NodeImage(id='dh_ps_mysql', name='mysql', driver=self.connection.driver)
+		return [webimage, mysqlimage]
 
 	def list_sizes(self, **kwargs):
-		raise NotImplementedError
+		raise NotImplementedError, \
+			'DreamHost Private Servers can be any size between 150MB and 4000MB'
 
 	def list_locations(self, **kwargs):
-		raise NotImplementedError
+		raise NotImplementedError, \
+			'You cannot select a location for DreamHost Private Servers at this time.'
 
 
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 59ed32f..8301aa6 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -35,66 +35,76 @@ class DreamhostTest(unittest.TestCase):
 		DreamhostMockHttp.use_param = 'cmd'
 		self.driver = DreamhostNodeDriver('foo')
 
-	def test_list_nodes(self):
+	def test_list_images(self):
 		"""
-		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
-			-	account_id: 000000
-				ip: 75.119.203.51
-				memory_mb: 500
-				ps: ps22174
-				start_date: 2010-02-25
-				type: web
-			-	account_id: 000000
-				ip: 75.119.203.52
-				memory_mb: 1500
-				ps: ps22175
-				start_date: 2010-02-25
-				type: mysql
+		Test list_images for DreamHost PS driver.  DreamHost PS supports two
+		'images': Web and Mysql.  There is not an api call to get these, so
+		they're just hard coded into the driver.
 		"""
+		images = self.driver.list_images()
+		self.assertEqual(len(images), 2)
+		self.assertEqual(images[0].id, 'dh_ps_web')
+		self.assertEqual(images[0].name, 'web')
+		self.assertEqual(images[1].id, 'dh_ps_mysql')
+		self.assertEqual(images[1].name, 'mysql')
 
-		nodes = self.driver.list_nodes()
-		self.assertEqual(len(nodes), 2)
-		web_node = nodes[0]
-		mysql_node = nodes[1]
-		# Web node tests
-		self.assertEqual(web_node.id, ps22174)
-		#self.assertEqual(web_node.name, ps22174)
-		self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		self.assertTrue('75.119.203.51' in web_node.public_ip)
-		self.assertTrue(
-			web_node.extra.has_key('current_size') and
-			web_node.extra['current_size'] == 500
-		)
-		self.assertTrue(
-			web_node.extra.has_key('account_id') and
-			web_node.extra['account_id'] == '000000'
-		)
-		self.assertTrue(
-			web_node.extra.has_key('type') and
-			web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
-		)
-		# MySql node tests
-		self.assertEqual(web_node.id, ps22175)
-		#self.assertEqual(web_node.name, ps22175)
-		self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		self.assertTrue('75.119.203.52' in web_node.public_ip)
-		self.assertTrue(
-			web_node.extra.has_key('current_size') and
-			web_node.extra['current_size'] == 1500
-		)
-		self.assertTrue(
-			web_node.extra.has_key('account_id') and
-			web_node.extra['account_id'] == '000000'
-		)
-		self.assertTrue(
-			web_node.extra.has_key('type') and
-			web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
-		)
+	#def test_list_nodes(self):
+		#"""
+		#Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
+			#-	account_id: 000000
+				#ip: 75.119.203.51
+				#memory_mb: 500
+				#ps: ps22174
+				#start_date: 2010-02-25
+				#type: web
+			#-	account_id: 000000
+				#ip: 75.119.203.52
+				#memory_mb: 1500
+				#ps: ps22175
+				#start_date: 2010-02-25
+				#type: mysql
+		#"""
+
+		#nodes = self.driver.list_nodes()
+		#self.assertEqual(len(nodes), 2)
+		#web_node = nodes[0]
+		#mysql_node = nodes[1]
+		## Web node tests
+		#self.assertEqual(web_node.id, ps22174)
+		##self.assertEqual(web_node.name, ps22174)
+		#self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		#self.assertTrue('75.119.203.51' in web_node.public_ip)
+		#self.assertTrue(
+			#web_node.extra.has_key('current_size') and
+			#web_node.extra['current_size'] == 500
+		#)
+		#self.assertTrue(
+			#web_node.extra.has_key('account_id') and
+			#web_node.extra['account_id'] == '000000'
+		#)
+		#self.assertTrue(
+			#web_node.extra.has_key('type') and
+			#web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
+		#)
+		## MySql node tests
+		#self.assertEqual(web_node.id, ps22175)
+		##self.assertEqual(web_node.name, ps22175)
+		#self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		#self.assertTrue('75.119.203.52' in web_node.public_ip)
+		#self.assertTrue(
+			#web_node.extra.has_key('current_size') and
+			#web_node.extra['current_size'] == 1500
+		#)
+		#self.assertTrue(
+			#web_node.extra.has_key('account_id') and
+			#web_node.extra['account_id'] == '000000'
+		#)
+		#self.assertTrue(
+			#web_node.extra.has_key('type') and
+			#web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
+		#)
 
 
-	#def test_list_images(self):
-		#raise NotImplementedError
-	
 	#def test_create_node(self):
 		#raise NotImplementedError
 
-- 
1.5.4.1


From eba1f82f481ba697b53e94bb59557eed6a9c14d0 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Tue, 2 Mar 2010 15:58:24 -0800
Subject: [PATCH] Unit Test: dreamhost list_nodes FAILS

-Added Dreamhost to libcloud/proivders.py and libcloud/types.py
-Re-enabled the list_nodes test in test/test_dreamhost.py
-Implemented list_nodes in drivers/dreamhost.py

TODO
-Implement _list_nodes for DreamhostMockHttp
-Implement Dreamhost response parsing
-Implement Dreamhost response -> Node
---
 libcloud/drivers/dreamhost.py |   23 +++++++-
 libcloud/providers.py         |    2 +
 libcloud/types.py             |    2 +
 test/test_dreamhost.py        |  110 ++++++++++++++++++++--------------------
 4 files changed, 79 insertions(+), 58 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index a2ed686..c422a44 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -40,16 +40,26 @@ class DreamhostConnection(ConnectionKey):
 
 	host = 'api.dreamhost.com'
 	responseCls = DreamhostResponse
+	format = 'json'
 
 	def add_default_params(self, params):
-		#self.key
-		raise NotImplementedError
+		"""
+		Add key and format parameters to the request.  Eventually should add
+		unique_id to prevent re-execution of a single request.
+		"""
+		params['key'] = self.key
+		params['format'] = self.format
+		#params['unique_id'] = generate_unique_id()
+		return params
 
 
 class DreamhostNodeDriver(NodeDriver):
 	"""
 	Node Driver for DreamHost PS
 	"""
+	type = Provider.DREAMHOST
+	name = "Dreamhost"
+	connectionCls = DreamhostConnection
 
 	def create_node(self, **kwargs):
 		raise NotImplementedError
@@ -61,9 +71,16 @@ class DreamhostNodeDriver(NodeDriver):
 		raise NotImplementedError
 
 	def list_nodes(self, **kwargs):
-		raise NotImplementedError
+		"""
+		Returns a list of all nodes this API key has access to.
+		"""
+		data = self.connection.request('/', { 'cmd': 'dreamhost_ps-list_ps' })
+		return [self._to_node(n) for n in data]
 
 	def list_images(self, **kwargs):
+		"""
+		Returns a list of the different kinds of DreamHost PS you can create.
+		"""
 		webimage = NodeImage(id='dh_ps_web', name='web', driver=self.connection.driver)
 		mysqlimage = NodeImage(id='dh_ps_mysql', name='mysql', driver=self.connection.driver)
 		return [webimage, mysqlimage]
diff --git a/libcloud/providers.py b/libcloud/providers.py
index 3e5559e..e94f3e0 100644
--- a/libcloud/providers.py
+++ b/libcloud/providers.py
@@ -49,6 +49,8 @@ DRIVERS = {
         ('libcloud.drivers.ec2', 'EucNodeDriver'),
     Provider.IBM:
         ('libcloud.drivers.ibm', 'IBMNodeDriver'),
+    Provider.DREAMHOST:
+        ('libcloud.drivers.dreamhost', 'DreamhostNodeDriver'),
 }
 
 def get_driver(provider):
diff --git a/libcloud/types.py b/libcloud/types.py
index 85f358c..3b3781d 100644
--- a/libcloud/types.py
+++ b/libcloud/types.py
@@ -32,6 +32,7 @@ class Provider(object):
     @cvar VCLOUD: vmware vCloud
     @cvar RIMUHOSTING: RimuHosting.com
     @cvar ECP: Enomaly
+    @cvar DREAMHOST: DreamHost Private Server
     """
     DUMMY = 0
     EC2 = 1  # deprecated name
@@ -51,6 +52,7 @@ class Provider(object):
     EUCALYPTUS = 13
     ECP = 14
     IBM = 15
+    DREAMHOST = 16
 
 class NodeState(object):
     """
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 8301aa6..18c7062 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -48,61 +48,61 @@ class DreamhostTest(unittest.TestCase):
 		self.assertEqual(images[1].id, 'dh_ps_mysql')
 		self.assertEqual(images[1].name, 'mysql')
 
-	#def test_list_nodes(self):
-		#"""
-		#Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
-			#-	account_id: 000000
-				#ip: 75.119.203.51
-				#memory_mb: 500
-				#ps: ps22174
-				#start_date: 2010-02-25
-				#type: web
-			#-	account_id: 000000
-				#ip: 75.119.203.52
-				#memory_mb: 1500
-				#ps: ps22175
-				#start_date: 2010-02-25
-				#type: mysql
-		#"""
-
-		#nodes = self.driver.list_nodes()
-		#self.assertEqual(len(nodes), 2)
-		#web_node = nodes[0]
-		#mysql_node = nodes[1]
-		## Web node tests
-		#self.assertEqual(web_node.id, ps22174)
-		##self.assertEqual(web_node.name, ps22174)
-		#self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		#self.assertTrue('75.119.203.51' in web_node.public_ip)
-		#self.assertTrue(
-			#web_node.extra.has_key('current_size') and
-			#web_node.extra['current_size'] == 500
-		#)
-		#self.assertTrue(
-			#web_node.extra.has_key('account_id') and
-			#web_node.extra['account_id'] == '000000'
-		#)
-		#self.assertTrue(
-			#web_node.extra.has_key('type') and
-			#web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
-		#)
-		## MySql node tests
-		#self.assertEqual(web_node.id, ps22175)
-		##self.assertEqual(web_node.name, ps22175)
-		#self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		#self.assertTrue('75.119.203.52' in web_node.public_ip)
-		#self.assertTrue(
-			#web_node.extra.has_key('current_size') and
-			#web_node.extra['current_size'] == 1500
-		#)
-		#self.assertTrue(
-			#web_node.extra.has_key('account_id') and
-			#web_node.extra['account_id'] == '000000'
-		#)
-		#self.assertTrue(
-			#web_node.extra.has_key('type') and
-			#web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
-		#)
+	def test_list_nodes(self):
+		"""
+		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
+			-	account_id: 000000
+				ip: 75.119.203.51
+				memory_mb: 500
+				ps: ps22174
+				start_date: 2010-02-25
+				type: web
+			-	account_id: 000000
+				ip: 75.119.203.52
+				memory_mb: 1500
+				ps: ps22175
+				start_date: 2010-02-25
+				type: mysql
+		"""
+
+		nodes = self.driver.list_nodes()
+		self.assertEqual(len(nodes), 2)
+		web_node = nodes[0]
+		mysql_node = nodes[1]
+		# Web node tests
+		self.assertEqual(web_node.id, ps22174)
+		#self.assertEqual(web_node.name, ps22174)
+		self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		self.assertTrue('75.119.203.51' in web_node.public_ip)
+		self.assertTrue(
+			web_node.extra.has_key('current_size') and
+			web_node.extra['current_size'] == 500
+		)
+		self.assertTrue(
+			web_node.extra.has_key('account_id') and
+			web_node.extra['account_id'] == '000000'
+		)
+		self.assertTrue(
+			web_node.extra.has_key('type') and
+			web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
+		)
+		# MySql node tests
+		self.assertEqual(web_node.id, ps22175)
+		#self.assertEqual(web_node.name, ps22175)
+		self.assertEqual(web_node.state, NodeState.UNKNOWN)
+		self.assertTrue('75.119.203.52' in web_node.public_ip)
+		self.assertTrue(
+			web_node.extra.has_key('current_size') and
+			web_node.extra['current_size'] == 1500
+		)
+		self.assertTrue(
+			web_node.extra.has_key('account_id') and
+			web_node.extra['account_id'] == '000000'
+		)
+		self.assertTrue(
+			web_node.extra.has_key('type') and
+			web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
+		)
 
 
 	#def test_create_node(self):
-- 
1.5.4.1


From 33243d4e3ffc75a7e4d176b212b50254348b3cb2 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Tue, 2 Mar 2010 17:02:31 -0800
Subject: [PATCH] Began writing DreamhostMockHttp

-JSON used for serialization. Other serialization methods are possible
-DreamhostMockHttp's _dreamhost_ps_list_ps method is written
-MockHttp's request method tweaked to replace '-' with '_' in parameter
---
 libcloud/drivers/dreamhost.py |    5 +++++
 test/__init__.py              |    2 +-
 test/test_dreamhost.py        |   24 +++++++++++++++++++++++-
 3 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index c422a44..3fa6936 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -22,6 +22,11 @@ from libcloud.types import Provider, NodeState
 #from zope.interface import implements
 import uuid
 
+# JSON is included in the standard library starting with Python 2.6.  For 2.5
+# and 2.4, there's a simplejson egg at: http://pypi.python.org/pypi/simplejson
+try: import json
+except: import simplejson as json
+
 class DreamhostResponse(Response):
 	"""
 	Response class for DreamHost PS
diff --git a/test/__init__.py b/test/__init__.py
index 2645fc6..9873810 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -119,7 +119,7 @@ class MockHttp(object):
         if self.type:
             meth_name = '%s_%s' % (meth_name, self.type) 
         if self.use_param:
-            param = qs[self.use_param][0].replace('.', '_')
+            param = qs[self.use_param][0].replace('.', '_').replace('-','_')
             meth_name = '%s_%s' % (meth_name, param)
         meth = getattr(self, meth_name)
         status, body, headers, reason = meth(method, url, body, headers)
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 18c7062..dcbb913 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -21,6 +21,9 @@ from libcloud.base import Node, NodeImage, NodeSize
 
 import httplib
 
+try: import json
+except: import simplejson as json
+
 from test import MockHttp, multipleresponse, TestCaseMixin
 from secrets import DREAMHOST_KEY
 
@@ -122,7 +125,26 @@ class DreamhostTest(unittest.TestCase):
 	
 class DreamhostMockHttp(MockHttp):
 	#methods should be: _command_name:
-	pass
+	def _dreamhost_ps_list_ps(self, method, url, body, headers):
+		data = [{
+			'account_id' : '000000',
+			'ip': '75.119.203.51',
+			'memory_mb' : '500',
+			'ps' : 'ps22174',
+			'start_date' : '2010-02-25',
+			'type' : 'web'
+		},
+		{
+			'account_id' : '000000',
+			'ip' : '75.119.203.52',
+			'memory_mb' : '1500',
+			'ps' : 'ps22175',
+			'start_date' : '2010-02-25',
+			'type' : 'mysql'
+		}]
+		result = 'success'
+		body = json.dumps({'data' : data, 'result' : result})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
 if __name__ == '__main__':
 	sys.exit(unittest.main())
-- 
1.5.4.1


From 7a4fec8525c5c00ffb2c1f019d5232e2924c01d6 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Tue, 2 Mar 2010 17:59:17 -0800
Subject: [PATCH] Unit Test: dreamhost test_list_nodes succeeds

-Implemented necessary response-processing stuff in the dreamhost driver
-Removed some unnecessary docstrings from dreamhost driver
-Corrected tests and mock response in dreamhost unit tests for test_list_nodes
---
 libcloud/drivers/dreamhost.py |   30 ++++++++++++++++++++++--------
 test/test_dreamhost.py        |   35 +++++++++++++++++------------------
 2 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 3fa6936..6f4d114 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -33,7 +33,10 @@ class DreamhostResponse(Response):
 	"""
 
 	def parse_body(self):
-		raise NotImplementedError
+		resp = json.loads(self.body)
+		if resp['result'] != 'success':
+			raise Exception(self.parse_error(resp))
+		return resp['data']
 
 	def parse_error(self):
 		raise NotImplementedError
@@ -76,16 +79,10 @@ class DreamhostNodeDriver(NodeDriver):
 		raise NotImplementedError
 
 	def list_nodes(self, **kwargs):
-		"""
-		Returns a list of all nodes this API key has access to.
-		"""
-		data = self.connection.request('/', { 'cmd': 'dreamhost_ps-list_ps' })
+		data = self.connection.request('/', { 'cmd': 'dreamhost_ps-list_ps' }).object
 		return [self._to_node(n) for n in data]
 
 	def list_images(self, **kwargs):
-		"""
-		Returns a list of the different kinds of DreamHost PS you can create.
-		"""
 		webimage = NodeImage(id='dh_ps_web', name='web', driver=self.connection.driver)
 		mysqlimage = NodeImage(id='dh_ps_mysql', name='mysql', driver=self.connection.driver)
 		return [webimage, mysqlimage]
@@ -98,4 +95,21 @@ class DreamhostNodeDriver(NodeDriver):
 		raise NotImplementedError, \
 			'You cannot select a location for DreamHost Private Servers at this time.'
 
+	def _to_node(self, data):
+		"""
+		Convert the data from a DreamhostResponse object into a Node
+		"""
+		return Node(
+			id = data['ps'],
+			name = None,
+			state = NodeState.UNKNOWN,
+			public_ip = [data['ip']],
+			private_ip = [],
+			driver = self.connection.driver,
+			extra = {
+				'current_size' : data['memory_mb'],
+				'account_id' : data['account_id'],
+				'type' : data['type']
+			}
+		)
 
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index dcbb913..cae45d8 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -72,9 +72,9 @@ class DreamhostTest(unittest.TestCase):
 		self.assertEqual(len(nodes), 2)
 		web_node = nodes[0]
 		mysql_node = nodes[1]
+
 		# Web node tests
-		self.assertEqual(web_node.id, ps22174)
-		#self.assertEqual(web_node.name, ps22174)
+		self.assertEqual(web_node.id, 'ps22174')
 		self.assertEqual(web_node.state, NodeState.UNKNOWN)
 		self.assertTrue('75.119.203.51' in web_node.public_ip)
 		self.assertTrue(
@@ -83,28 +83,27 @@ class DreamhostTest(unittest.TestCase):
 		)
 		self.assertTrue(
 			web_node.extra.has_key('account_id') and
-			web_node.extra['account_id'] == '000000'
+			web_node.extra['account_id'] == 000000
 		)
 		self.assertTrue(
 			web_node.extra.has_key('type') and
-			web_node.extra['type'] == DreamhostNodeDriver.DHType.WEB
+			web_node.extra['type'] == 'web'
 		)
 		# MySql node tests
-		self.assertEqual(web_node.id, ps22175)
-		#self.assertEqual(web_node.name, ps22175)
-		self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		self.assertTrue('75.119.203.52' in web_node.public_ip)
+		self.assertEqual(mysql_node.id, 'ps22175')
+		self.assertEqual(mysql_node.state, NodeState.UNKNOWN)
+		self.assertTrue('75.119.203.52' in mysql_node.public_ip)
 		self.assertTrue(
-			web_node.extra.has_key('current_size') and
-			web_node.extra['current_size'] == 1500
+			mysql_node.extra.has_key('current_size') and
+			mysql_node.extra['current_size'] == 1500
 		)
 		self.assertTrue(
-			web_node.extra.has_key('account_id') and
-			web_node.extra['account_id'] == '000000'
+			mysql_node.extra.has_key('account_id') and
+			mysql_node.extra['account_id'] == 000000
 		)
 		self.assertTrue(
-			web_node.extra.has_key('type') and
-			web_node.extra['type'] == DreamhostNodeDriver.DHType.MYSQL
+			mysql_node.extra.has_key('type') and
+			mysql_node.extra['type'] == 'mysql'
 		)
 
 
@@ -127,17 +126,17 @@ class DreamhostMockHttp(MockHttp):
 	#methods should be: _command_name:
 	def _dreamhost_ps_list_ps(self, method, url, body, headers):
 		data = [{
-			'account_id' : '000000',
+			'account_id' : 000000,
 			'ip': '75.119.203.51',
-			'memory_mb' : '500',
+			'memory_mb' : 500,
 			'ps' : 'ps22174',
 			'start_date' : '2010-02-25',
 			'type' : 'web'
 		},
 		{
-			'account_id' : '000000',
+			'account_id' : 000000,
 			'ip' : '75.119.203.52',
-			'memory_mb' : '1500',
+			'memory_mb' : 1500,
 			'ps' : 'ps22175',
 			'start_date' : '2010-02-25',
 			'type' : 'mysql'
-- 
1.5.4.1


From 48a7f97667d3731aba3956ce4adfb69eb0927bab Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 3 Mar 2010 08:24:45 -0800
Subject: [PATCH] Rearranged Dreamhost Unit Tests

-Moved test_list_images below test_list_nodes and the tests for
 manipulating an existing node.
-Wrote "tests" for list_sizes and list_locations that except the
 NotImplementedError -- thoes aren't supported by DreamHost PS
---
 test/test_dreamhost.py |   40 +++++++++++++++++++++++-----------------
 1 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index cae45d8..fdf22fc 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -38,19 +38,6 @@ class DreamhostTest(unittest.TestCase):
 		DreamhostMockHttp.use_param = 'cmd'
 		self.driver = DreamhostNodeDriver('foo')
 
-	def test_list_images(self):
-		"""
-		Test list_images for DreamHost PS driver.  DreamHost PS supports two
-		'images': Web and Mysql.  There is not an api call to get these, so
-		they're just hard coded into the driver.
-		"""
-		images = self.driver.list_images()
-		self.assertEqual(len(images), 2)
-		self.assertEqual(images[0].id, 'dh_ps_web')
-		self.assertEqual(images[0].name, 'web')
-		self.assertEqual(images[1].id, 'dh_ps_mysql')
-		self.assertEqual(images[1].name, 'mysql')
-
 	def test_list_nodes(self):
 		"""
 		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
@@ -116,11 +103,30 @@ class DreamhostTest(unittest.TestCase):
 	#def test_reboot_node(self):
 		#raise NotImplementedError
 
-	#def test_list_sizes(self):
-		#raise NotImplementedError
+	def test_list_images(self):
+		"""
+		Test list_images for DreamHost PS driver.  DreamHost PS supports two
+		'images': Web and Mysql.  There is not an api call to get these, so
+		they're just hard coded into the driver.
+		"""
+		images = self.driver.list_images()
+		self.assertEqual(len(images), 2)
+		self.assertEqual(images[0].id, 'dh_ps_web')
+		self.assertEqual(images[0].name, 'web')
+		self.assertEqual(images[1].id, 'dh_ps_mysql')
+		self.assertEqual(images[1].name, 'mysql')
 
-	#def test_list_locations(self):
-		#raise NotImplementedError
+	def test_list_sizes(self):
+		try:
+			self.driver.list_sizes()
+		except NotImplementedError:
+			pass
+
+	def test_list_locations(self):
+		try:
+			self.driver.list_locations()
+		except NotImplementedError:
+			pass
 	
 class DreamhostMockHttp(MockHttp):
 	#methods should be: _command_name:
-- 
1.5.4.1


From db081d5c0a34a35396a6d35ec4c9366321978866 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 3 Mar 2010 11:47:47 -0800
Subject: [PATCH] Unit Test: dreamhost reboot_node SUCCESS

-DreamHost driver's reboot_node written.
-DreamhostMockHttp _dreamhost_ps_reboot written.
-Note added to DreamHost driver's create_node mentioning problems.
-Placeholder for test for a failed reboot written.
---
 libcloud/drivers/dreamhost.py |   10 +++++++---
 test/test_dreamhost.py        |   35 +++++++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 6f4d114..6d94cb4 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -75,11 +75,15 @@ class DreamhostNodeDriver(NodeDriver):
 	def destroy_node(self, **kwargs):
 		raise NotImplementedError
 
-	def reboot_node(self, **kwargs):
-		raise NotImplementedError
+	def reboot_node(self, node):
+		params = {
+			'cmd' : 'dreamhost_ps-reboot',
+			'ps' : node.id
+		}
+		return self.connection.request('/', params).status
 
 	def list_nodes(self, **kwargs):
-		data = self.connection.request('/', { 'cmd': 'dreamhost_ps-list_ps' }).object
+		data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_ps' }).object
 		return [self._to_node(n) for n in data]
 
 	def list_images(self, **kwargs):
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index fdf22fc..bbc216e 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -93,15 +93,38 @@ class DreamhostTest(unittest.TestCase):
 			mysql_node.extra['type'] == 'mysql'
 		)
 
-
 	#def test_create_node(self):
-		#raise NotImplementedError
+		#"""
+		#Test create_node for DreamHost PS driver.
+		#This needs serious looking at; DH API asks the user whether they want
+		#a WEB or MYSQL ps and whether to move all their data to the (web) PS.
+		#It does NOT accept a name, size, or location.  It does not return any
+		#information about the requested PS.  Once the PS is ready it will
+		#appear in the list generated by list_ps, but we don't have enough
+		#information to create a libcloud node until that time.
+		#"""
+		#node = self.driver.create_node(
+			#name = 'unused',
+			#size = 'unused',
+			#image = self.driver.list_images()[0],
+			#location = 'unused',
+			#auth = 'usused'
+		#)
 
 	#def test_destroy_node(self):
 		#raise NotImplementedError
 
-	#def test_reboot_node(self):
-		#raise NotImplementedError
+	def test_reboot_node(self):
+		"""
+		Test reboot_node for DreamHost PS driver.
+		"""
+		node = self.driver.list_nodes()[0]
+		self.assertTrue(self.driver.reboot_node(node))
+
+	#def test_reboot_node_failure(self):
+		#set dhmockhttp to return a failure
+		#node = self.driver.list_nodes()[0]
+		#self.assertFalse(self.driver.reboot_node(node))
 
 	def test_list_images(self):
 		"""
@@ -151,6 +174,10 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : data, 'result' : result})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _dreamhost_ps_reboot(self, method, url, body, headers):
+		body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 if __name__ == '__main__':
 	sys.exit(unittest.main())
 
-- 
1.5.4.1


From c8bf21f171330327767adb61f1919d35650fdc1f Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 3 Mar 2010 12:44:40 -0800
Subject: [PATCH] Unit Test: dreamhost destroy_node passes

-Implemented dreamhost driver's destroy_node method and corresponding
 unit test and mockhttp methods.
---
 libcloud/drivers/dreamhost.py |    8 ++++++--
 test/test_dreamhost.py        |   14 +++++++++++---
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 6d94cb4..0c54304 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -72,8 +72,12 @@ class DreamhostNodeDriver(NodeDriver):
 	def create_node(self, **kwargs):
 		raise NotImplementedError
 
-	def destroy_node(self, **kwargs):
-		raise NotImplementedError
+	def destroy_node(self, node):
+		params = {
+			'cmd' : 'dreamhost_ps-remove_ps',
+			'ps' : node.id
+		}
+		return self.connection.request('/', params).status
 
 	def reboot_node(self, node):
 		params = {
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index bbc216e..62bdf25 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -111,8 +111,12 @@ class DreamhostTest(unittest.TestCase):
 			#auth = 'usused'
 		#)
 
-	#def test_destroy_node(self):
-		#raise NotImplementedError
+	def test_destroy_node(self):
+		"""
+		Test destroy_node for DreamHost PS driver
+		"""
+		node = self.driver.list_nodes()[0]
+		self.assertTrue(self.driver.destroy_node(node))
 
 	def test_reboot_node(self):
 		"""
@@ -152,7 +156,7 @@ class DreamhostTest(unittest.TestCase):
 			pass
 	
 class DreamhostMockHttp(MockHttp):
-	#methods should be: _command_name:
+
 	def _dreamhost_ps_list_ps(self, method, url, body, headers):
 		data = [{
 			'account_id' : 000000,
@@ -178,6 +182,10 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _dreamhost_ps_remove_ps(self, method, url, body, headers):
+		body = json.dumps({'data' : 'removed_web', 'result' : 'success'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 if __name__ == '__main__':
 	sys.exit(unittest.main())
 
-- 
1.5.4.1


From 406ffacb40671535fe17f697d5099a55e5961b7e Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 3 Mar 2010 14:59:03 -0800
Subject: [PATCH] Unit Tests: dreamhost api-failure tests successful

-Implemented DreamhostResponse api_parse_error for when there is an API error.
-Created DreamhostAPIException execption for api errors
-Wrote tests for api errors causing failure for destroy_node and reboot_node

TODO
-Find out when DH API returns an HTTP error (401 for bad creds?).
-Implement DreamhostResponse parse_error for handling HTTP errors.
-Add in Mixin unit tests
-Decied how best to handle create_node with the DreamHost API
-Add a "resize_node" to resize node after creation.
---
 libcloud/drivers/dreamhost.py |   24 +++++++++++++++++++++---
 test/test_dreamhost.py        |   31 +++++++++++++++++++++++++++----
 2 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 0c54304..2bc97d2 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -27,6 +27,12 @@ import uuid
 try: import json
 except: import simplejson as json
 
+class DreamhostAPIException(Exception):
+    def __str__(self):
+        return self.args[0]
+    def __repr__(self):
+        return "<DreamhostException '%s'>" % (self.args[0])
+
 class DreamhostResponse(Response):
 	"""
 	Response class for DreamHost PS
@@ -35,12 +41,18 @@ class DreamhostResponse(Response):
 	def parse_body(self):
 		resp = json.loads(self.body)
 		if resp['result'] != 'success':
-			raise Exception(self.parse_error(resp))
+			raise Exception(self.api_parse_error(resp))
 		return resp['data']
 
 	def parse_error(self):
 		raise NotImplementedError
 
+	def api_parse_error(self, response):
+		if response.has_key('data'):
+			raise DreamhostAPIException(response['data'])
+		else:
+			raise DreamhostAPIException("Unknown problem: %s" % (self.body))
+
 class DreamhostConnection(ConnectionKey):
 	"""
 	Connection class to connect to DreamHost's API servers
@@ -77,14 +89,20 @@ class DreamhostNodeDriver(NodeDriver):
 			'cmd' : 'dreamhost_ps-remove_ps',
 			'ps' : node.id
 		}
-		return self.connection.request('/', params).status
+		try:
+			return self.connection.request('/', params).success
+		except DreamhostAPIException:
+			return False
 
 	def reboot_node(self, node):
 		params = {
 			'cmd' : 'dreamhost_ps-reboot',
 			'ps' : node.id
 		}
-		return self.connection.request('/', params).status
+		try:
+			return self.connection.request('/', params).success
+		except DreamhostAPIException:
+			return False
 
 	def list_nodes(self, **kwargs):
 		data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_ps' }).object
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 62bdf25..1a9f4a1 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -35,6 +35,7 @@ class DreamhostTest(unittest.TestCase):
 			None,
 			DreamhostMockHttp
 		)
+		DreamhostMockHttp.type = None
 		DreamhostMockHttp.use_param = 'cmd'
 		self.driver = DreamhostNodeDriver('foo')
 
@@ -118,6 +119,15 @@ class DreamhostTest(unittest.TestCase):
 		node = self.driver.list_nodes()[0]
 		self.assertTrue(self.driver.destroy_node(node))
 
+	def test_destroy_node_failure(self):
+		"""
+		Test destroy_node failure for DreamHost PS driver
+		"""
+		node = self.driver.list_nodes()[0]
+
+		DreamhostMockHttp.type = 'API_FAILURE'
+		self.assertFalse(self.driver.destroy_node(node))
+
 	def test_reboot_node(self):
 		"""
 		Test reboot_node for DreamHost PS driver.
@@ -125,10 +135,14 @@ class DreamhostTest(unittest.TestCase):
 		node = self.driver.list_nodes()[0]
 		self.assertTrue(self.driver.reboot_node(node))
 
-	#def test_reboot_node_failure(self):
-		#set dhmockhttp to return a failure
-		#node = self.driver.list_nodes()[0]
-		#self.assertFalse(self.driver.reboot_node(node))
+	def test_reboot_node_failure(self):
+		"""
+		Test reboot_node failure for DreamHost PS driver
+		"""
+		node = self.driver.list_nodes()[0]
+
+		DreamhostMockHttp.type = 'API_FAILURE'
+		self.assertFalse(self.driver.reboot_node(node))
 
 	def test_list_images(self):
 		"""
@@ -182,10 +196,19 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _API_FAILURE_dreamhost_ps_reboot(self, method, url, body, headers):
+		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 	def _dreamhost_ps_remove_ps(self, method, url, body, headers):
 		body = json.dumps({'data' : 'removed_web', 'result' : 'success'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _API_FAILURE_dreamhost_ps_remove_ps(self, method, url, body, headers):
+		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+
 if __name__ == '__main__':
 	sys.exit(unittest.main())
 
-- 
1.5.4.1


From a07275f11532d24b32af5f7f8b07a8b02bb37873 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 3 Mar 2010 17:34:23 -0800
Subject: [PATCH] Updated DreamhostNodeDriver's error handling

DreamHost API should always return Status: 200 OK; if it doesn't then
probably something is wrong on our end.  Error handling now throws an
InvalidCredsException when you try to give it a bad key.

Also tweaked reboot_node and destroy_node so they return a bool insead
of a bound method that returns a bool *facepalm*.
---
 libcloud/drivers/dreamhost.py |   13 ++++++++-----
 test/test_dreamhost.py        |   19 +++++++++++++++++--
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 2bc97d2..3226649 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -18,7 +18,7 @@ DreamHost Driver
 from libcloud.interface import INodeDriver
 from libcloud.base import ConnectionKey, Response, NodeDriver, Node
 from libcloud.base import NodeSize, NodeImage, NodeLocation
-from libcloud.types import Provider, NodeState
+from libcloud.types import Provider, NodeState, InvalidCredsException
 #from zope.interface import implements
 import uuid
 
@@ -45,11 +45,14 @@ class DreamhostResponse(Response):
 		return resp['data']
 
 	def parse_error(self):
-		raise NotImplementedError
+		raise Exception
 
 	def api_parse_error(self, response):
 		if response.has_key('data'):
-			raise DreamhostAPIException(response['data'])
+			if response['data'] == 'invalid_api_key':
+				raise InvalidCredsException, "Oops!  You've entered an invalid API key"
+			else:
+				raise DreamhostAPIException(response['data'])
 		else:
 			raise DreamhostAPIException("Unknown problem: %s" % (self.body))
 
@@ -90,7 +93,7 @@ class DreamhostNodeDriver(NodeDriver):
 			'ps' : node.id
 		}
 		try:
-			return self.connection.request('/', params).success
+			return self.connection.request('/', params).success()
 		except DreamhostAPIException:
 			return False
 
@@ -100,7 +103,7 @@ class DreamhostNodeDriver(NodeDriver):
 			'ps' : node.id
 		}
 		try:
-			return self.connection.request('/', params).success
+			return self.connection.request('/', params).success()
 		except DreamhostAPIException:
 			return False
 
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 1a9f4a1..d717c13 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -16,7 +16,7 @@ import sys
 import unittest
 
 from libcloud.drivers.dreamhost import DreamhostNodeDriver
-from libcloud.types import Provider, NodeState
+from libcloud.types import Provider, NodeState, InvalidCredsException
 from libcloud.base import Node, NodeImage, NodeSize
 
 import httplib
@@ -39,6 +39,18 @@ class DreamhostTest(unittest.TestCase):
 		DreamhostMockHttp.use_param = 'cmd'
 		self.driver = DreamhostNodeDriver('foo')
 
+	def test_invalid_creds(self):
+		"""
+		Tests the error-handling for passing a bad API Key to the DreamHost API
+		"""
+		DreamhostMockHttp.type = 'BAD_AUTH'
+		try:
+			self.driver.list_nodes()
+			self.assertTrue(False) # Above command should have thrown an InvalidCredsException
+		except InvalidCredsException:
+			self.assertTrue(True)
+
+
 	def test_list_nodes(self):
 		"""
 		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
@@ -171,6 +183,10 @@ class DreamhostTest(unittest.TestCase):
 	
 class DreamhostMockHttp(MockHttp):
 
+	def _BAD_AUTH_dreamhost_ps_list_ps(self, method, url, body, headers):
+		body = json.dumps({'data' : 'invalid_api_key', 'result' : 'error'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 	def _dreamhost_ps_list_ps(self, method, url, body, headers):
 		data = [{
 			'account_id' : 000000,
@@ -208,7 +224,6 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
-
 if __name__ == '__main__':
 	sys.exit(unittest.main())
 
-- 
1.5.4.1


From 1b2e1f0613444581efeea18bd1edff3fee8578a4 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Fri, 2 Apr 2010 13:22:15 -0700
Subject: [PATCH] Completed DreamHost driver's create_node

Reimplemented list_images to use DH's new API call
Added placeholder for resize_node
---
 libcloud/drivers/dreamhost.py |   45 ++++++++++++++++++++++++------
 test/test_dreamhost.py        |   61 ++++++++++++++++++++++++++---------------
 2 files changed, 75 insertions(+), 31 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 3226649..ae58d0c 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -28,10 +28,10 @@ try: import json
 except: import simplejson as json
 
 class DreamhostAPIException(Exception):
-    def __str__(self):
-        return self.args[0]
-    def __repr__(self):
-        return "<DreamhostException '%s'>" % (self.args[0])
+	def __str__(self):
+		return self.args[0]
+	def __repr__(self):
+		return "<DreamhostException '%s'>" % (self.args[0])
 
 class DreamhostResponse(Response):
 	"""
@@ -85,7 +85,23 @@ class DreamhostNodeDriver(NodeDriver):
 	connectionCls = DreamhostConnection
 
 	def create_node(self, **kwargs):
-		raise NotImplementedError
+		params = {
+			'cmd' : 'dreamhost_ps-add_ps',
+			'movedata' : kwargs['movedata'],
+			'type' : kwargs['type'].name
+		}
+		data = self.connection.request('/', params).object
+		return Node(
+			id = data['added_' + kwargs['type'].name],
+			name = data['added_' + kwargs['type'].name],
+			state = NodeState.PENDING,
+			public_ip = [],
+			private_ip = [],
+			driver = self.connection.driver,
+			extra = {
+				'type' : kwargs['type'].name
+			}
+		)
 
 	def destroy_node(self, node):
 		params = {
@@ -97,6 +113,11 @@ class DreamhostNodeDriver(NodeDriver):
 		except DreamhostAPIException:
 			return False
 
+	def resize_node(self, node, size):
+		raise NotImplementedError, \
+			'DreamHost Private Servers can be resized dynamically.  Check \
+			back soon for this added functionality!'
+
 	def reboot_node(self, node):
 		params = {
 			'cmd' : 'dreamhost_ps-reboot',
@@ -112,9 +133,15 @@ class DreamhostNodeDriver(NodeDriver):
 		return [self._to_node(n) for n in data]
 
 	def list_images(self, **kwargs):
-		webimage = NodeImage(id='dh_ps_web', name='web', driver=self.connection.driver)
-		mysqlimage = NodeImage(id='dh_ps_mysql', name='mysql', driver=self.connection.driver)
-		return [webimage, mysqlimage]
+		data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_images' }).object
+		images = []
+		for img in data:
+			images.append(NodeImage(
+				id = img['image'],
+				name = img['image'],
+				driver = self.connection.driver
+			))
+		return images
 
 	def list_sizes(self, **kwargs):
 		raise NotImplementedError, \
@@ -130,7 +157,7 @@ class DreamhostNodeDriver(NodeDriver):
 		"""
 		return Node(
 			id = data['ps'],
-			name = None,
+			name = data['ps'],
 			state = NodeState.UNKNOWN,
 			public_ip = [data['ip']],
 			private_ip = [],
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index d717c13..72b74b8 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -106,23 +106,25 @@ class DreamhostTest(unittest.TestCase):
 			mysql_node.extra['type'] == 'mysql'
 		)
 
-	#def test_create_node(self):
-		#"""
-		#Test create_node for DreamHost PS driver.
-		#This needs serious looking at; DH API asks the user whether they want
-		#a WEB or MYSQL ps and whether to move all their data to the (web) PS.
-		#It does NOT accept a name, size, or location.  It does not return any
-		#information about the requested PS.  Once the PS is ready it will
-		#appear in the list generated by list_ps, but we don't have enough
-		#information to create a libcloud node until that time.
-		#"""
-		#node = self.driver.create_node(
-			#name = 'unused',
-			#size = 'unused',
-			#image = self.driver.list_images()[0],
-			#location = 'unused',
-			#auth = 'usused'
-		#)
+	def test_create_node(self):
+		"""
+		Test create_node for DreamHost PS driver.
+		This is not remarkably compatible with libcloud.  The DH API allows
+		users to specify what image they want to create and whether to move
+		all their data to the (web) PS. It does NOT accept a name, size, or
+		location.  The only information it returns is the PS's context id
+		Once the PS is ready it will appear in the list generated by list_ps.
+		"""
+		new_node = self.driver.create_node(
+			type = self.driver.list_images()[0],
+			movedata = 'no'
+		)
+		self.assertEqual(new_node.id, 'ps12345')
+		self.assertEqual(new_node.state, NodeState.PENDING)
+		self.assertTrue(
+			new_node.extra.has_key('type') and
+			new_node.extra['type'] == 'web'
+		)
 
 	def test_destroy_node(self):
 		"""
@@ -158,15 +160,13 @@ class DreamhostTest(unittest.TestCase):
 
 	def test_list_images(self):
 		"""
-		Test list_images for DreamHost PS driver.  DreamHost PS supports two
-		'images': Web and Mysql.  There is not an api call to get these, so
-		they're just hard coded into the driver.
+		Test list_images for DreamHost PS driver.
 		"""
 		images = self.driver.list_images()
 		self.assertEqual(len(images), 2)
-		self.assertEqual(images[0].id, 'dh_ps_web')
+		self.assertEqual(images[0].id, 'web')
 		self.assertEqual(images[0].name, 'web')
-		self.assertEqual(images[1].id, 'dh_ps_mysql')
+		self.assertEqual(images[1].id, 'mysql')
 		self.assertEqual(images[1].name, 'mysql')
 
 	def test_list_sizes(self):
@@ -187,6 +187,10 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : 'invalid_api_key', 'result' : 'error'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _dreamhost_ps_add_ps(self, method, url, body, headers):
+		body = json.dumps({'data' : {'added_web' : 'ps12345'}, 'result' : 'success'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 	def _dreamhost_ps_list_ps(self, method, url, body, headers):
 		data = [{
 			'account_id' : 000000,
@@ -208,6 +212,19 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : data, 'result' : result})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _dreamhost_ps_list_images(self, method, url, body, headers):
+		data = [{
+			'description' : 'Private web server',
+			'image' : 'web'
+		},
+		{
+			'description' : 'Private MySQL server',
+			'image' : 'mysql'
+		}]
+		result = 'success'
+		body = json.dumps({'data' : data, 'result' : result})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 	def _dreamhost_ps_reboot(self, method, url, body, headers):
 		body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-- 
1.5.4.1


From 4c5024fe85a550fd6e2c82715e44c22ee176f612 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Mon, 5 Apr 2010 10:21:58 -0700
Subject: [PATCH] More work on dreamhost driver.

---
 libcloud/drivers/dreamhost.py |   15 ++++++++++++---
 test/test_dreamhost.py        |   24 ++++++++++++++++++++++++
 2 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index ae58d0c..5260d5d 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -114,9 +114,18 @@ class DreamhostNodeDriver(NodeDriver):
 			return False
 
 	def resize_node(self, node, size):
-		raise NotImplementedError, \
-			'DreamHost Private Servers can be resized dynamically.  Check \
-			back soon for this added functionality!'
+		if (size < 300 or size > 4000):
+			return False
+
+		params = {
+			'cmd' : 'dreamhost_ps-set_size',
+			'ps' : node.id,
+			'size' : size
+		}
+		try:
+			return self.connection.request('/', params).success()
+		except DreamhostAPIException:
+			return False
 
 	def reboot_node(self, node):
 		params = {
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 72b74b8..6fc5c3f 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -158,6 +158,22 @@ class DreamhostTest(unittest.TestCase):
 		DreamhostMockHttp.type = 'API_FAILURE'
 		self.assertFalse(self.driver.reboot_node(node))
 
+	def test_resize_node(self):
+		"""
+		Test resize_node for DreamHost PS driver
+		"""
+		node = self.driver.list_nodes()[0]
+		self.assertTrue(self.driver.resize_node(node, 400))
+
+	def test_resize_node_failure(self):
+		"""
+		Test reboot_node faliure for DreamHost PS driver
+		"""
+		node = self.driver.list_nodes()[0]
+
+		DreamhostMockHttp.type = 'API_FAILURE'
+		self.assertFalse(self.driver.resize_node(node, 400))
+
 	def test_list_images(self):
 		"""
 		Test list_images for DreamHost PS driver.
@@ -233,6 +249,14 @@ class DreamhostMockHttp(MockHttp):
 		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+	def _dreamhost_ps_set_size(self, method, url, body, headers):
+		body = json.dumps({'data' : {'memory-mb' : '500'}, 'result' : 'success'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+	def _API_FAILURE_dreamhost_ps_set_size(self, method, url, body, headers):
+		body = json.dumps({'data' : 'internal_error_setting_size', 'result' : 'error'})
+		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
 	def _dreamhost_ps_remove_ps(self, method, url, body, headers):
 		body = json.dumps({'data' : 'removed_web', 'result' : 'success'})
 		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-- 
1.5.4.1


From f729f440092c22cd3c3957aec88b2c052a76d140 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Tue, 27 Apr 2010 18:02:57 -0700
Subject: [PATCH] Making changes requested by Jeremy

* Made resize_node private (_resize_node).
* Added some hardcoded sizes ala the ec2 driver.
* Made create_node accept a size.
* Changed kwarg parameter from "type" to "image" in create_node.

TODO:
* Replace tabs with 4 spaces.
* Update DH wiki to reflect recent changes in the API for private servers.
---
 libcloud/drivers/dreamhost.py |   94 ++++++++++++++++++++++++++++++++---------
 test/test_dreamhost.py        |    9 ++--
 2 files changed, 79 insertions(+), 24 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index 5260d5d..c75c906 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -27,6 +27,54 @@ import uuid
 try: import json
 except: import simplejson as json
 
+"""
+DreamHost Private Servers can be resized on the fly, but Libcloud doesn't
+currently support extensions to its interface, so we'll put some basic sizes
+in for node creation.
+"""
+DH_PS_SIZES = {
+	'minimum': {
+		'id' : 'minimum',
+		'name' : 'Minimum DH PS size',
+		'ram' : 300,
+		'price' : 15,
+		'disk' : None,
+		'bandwidth' : None
+	},
+	'maximum': {
+		'id' : 'maximum',
+		'name' : 'Maximum DH PS size',
+		'ram' : 4000,
+		'price' : 200,
+		'disk' : None,
+		'bandwidth' : None
+	},
+	'default': {
+		'id' : 'default',
+		'name' : 'Default DH PS size',
+		'ram' : 2300,
+		'price' : 115,
+		'disk' : None,
+		'bandwidth' : None
+	},
+	'low': {
+		'id' : 'low',
+		'name' : 'DH PS with 1GB RAM',
+		'ram' : 1000,
+		'price' : 50,
+		'disk' : None,
+		'bandwidth' : None
+	},
+	'high': {
+		'id' : 'high',
+		'name' : 'DH PS with 3GB RAM',
+		'ram' : 3000,
+		'price' : 150,
+		'disk' : None,
+		'bandwidth' : None
+	},
+}
+
 class DreamhostAPIException(Exception):
 	def __str__(self):
 		return self.args[0]
@@ -83,23 +131,26 @@ class DreamhostNodeDriver(NodeDriver):
 	type = Provider.DREAMHOST
 	name = "Dreamhost"
 	connectionCls = DreamhostConnection
+	_sizes = DH_PS_SIZES
 
 	def create_node(self, **kwargs):
+		size = kwargs['size'].ram
 		params = {
 			'cmd' : 'dreamhost_ps-add_ps',
 			'movedata' : kwargs['movedata'],
-			'type' : kwargs['type'].name
+			'type' : kwargs['image'].name,
+			'size' : size
 		}
 		data = self.connection.request('/', params).object
 		return Node(
-			id = data['added_' + kwargs['type'].name],
-			name = data['added_' + kwargs['type'].name],
+			id = data['added_' + kwargs['image'].name],
+			name = data['added_' + kwargs['image'].name],
 			state = NodeState.PENDING,
 			public_ip = [],
 			private_ip = [],
 			driver = self.connection.driver,
 			extra = {
-				'type' : kwargs['type'].name
+				'type' : kwargs['image'].name
 			}
 		)
 
@@ -113,20 +164,6 @@ class DreamhostNodeDriver(NodeDriver):
 		except DreamhostAPIException:
 			return False
 
-	def resize_node(self, node, size):
-		if (size < 300 or size > 4000):
-			return False
-
-		params = {
-			'cmd' : 'dreamhost_ps-set_size',
-			'ps' : node.id,
-			'size' : size
-		}
-		try:
-			return self.connection.request('/', params).success()
-		except DreamhostAPIException:
-			return False
-
 	def reboot_node(self, node):
 		params = {
 			'cmd' : 'dreamhost_ps-reboot',
@@ -153,13 +190,30 @@ class DreamhostNodeDriver(NodeDriver):
 		return images
 
 	def list_sizes(self, **kwargs):
-		raise NotImplementedError, \
-			'DreamHost Private Servers can be any size between 150MB and 4000MB'
+		return [ NodeSize(driver=self.connection.driver, **i)
+			for i in self._sizes.values() ]
 
 	def list_locations(self, **kwargs):
 		raise NotImplementedError, \
 			'You cannot select a location for DreamHost Private Servers at this time.'
 
+	############################################
+	# Private Methods (helpers and extensions) #
+	############################################
+	def _resize_node(self, node, size):
+		if (size < 300 or size > 4000):
+			return False
+
+		params = {
+			'cmd' : 'dreamhost_ps-set_size',
+			'ps' : node.id,
+			'size' : size
+		}
+		try:
+			return self.connection.request('/', params).success()
+		except DreamhostAPIException:
+			return False
+
 	def _to_node(self, data):
 		"""
 		Convert the data from a DreamhostResponse object into a Node
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index 6fc5c3f..c51b739 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -116,8 +116,9 @@ class DreamhostTest(unittest.TestCase):
 		Once the PS is ready it will appear in the list generated by list_ps.
 		"""
 		new_node = self.driver.create_node(
-			type = self.driver.list_images()[0],
-			movedata = 'no'
+			image = self.driver.list_images()[0],
+			size = self.driver.list_sizes()[0],
+			movedata = 'no',
 		)
 		self.assertEqual(new_node.id, 'ps12345')
 		self.assertEqual(new_node.state, NodeState.PENDING)
@@ -163,7 +164,7 @@ class DreamhostTest(unittest.TestCase):
 		Test resize_node for DreamHost PS driver
 		"""
 		node = self.driver.list_nodes()[0]
-		self.assertTrue(self.driver.resize_node(node, 400))
+		self.assertTrue(self.driver._resize_node(node, 400))
 
 	def test_resize_node_failure(self):
 		"""
@@ -172,7 +173,7 @@ class DreamhostTest(unittest.TestCase):
 		node = self.driver.list_nodes()[0]
 
 		DreamhostMockHttp.type = 'API_FAILURE'
-		self.assertFalse(self.driver.resize_node(node, 400))
+		self.assertFalse(self.driver._resize_node(node, 400))
 
 	def test_list_images(self):
 		"""
-- 
1.5.4.1


From fda6296533936d470b65ef6fd2d08ff5eed7204d Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 28 Apr 2010 10:38:46 -0700
Subject: [PATCH] Added unit test for new list_sizes

---
 test/test_dreamhost.py |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index c51b739..cb3e73c 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -187,10 +187,14 @@ class DreamhostTest(unittest.TestCase):
 		self.assertEqual(images[1].name, 'mysql')
 
 	def test_list_sizes(self):
-		try:
-			self.driver.list_sizes()
-		except NotImplementedError:
-			pass
+		sizes = self.driver.list_sizes()
+		self.assertEqual(len(sizes), 5)
+
+		self.assertEqual(sizes[0].id, 'default')
+		self.assertEqual(sizes[0].bandwidth, None)
+		self.assertEqual(sizes[0].disk, None)
+		self.assertEqual(sizes[0].ram, 2300)
+		self.assertEqual(sizes[0].price, 115)
 
 	def test_list_locations(self):
 		try:
-- 
1.5.4.1


From 9b0ebba65993edf2557ce210aeb717c2e18ed2f4 Mon Sep 17 00:00:00 2001
From: Kyle Marsh <kylem@hq.newdream.net>
Date: Wed, 28 Apr 2010 11:03:13 -0700
Subject: [PATCH] Retabbed the Dreamhost files to use spaces

---
 libcloud/drivers/dreamhost.py |  372 ++++++++++++++++----------------
 test/test_dreamhost.py        |  476 ++++++++++++++++++++--------------------
 2 files changed, 424 insertions(+), 424 deletions(-)

diff --git a/libcloud/drivers/dreamhost.py b/libcloud/drivers/dreamhost.py
index c75c906..9001de6 100644
--- a/libcloud/drivers/dreamhost.py
+++ b/libcloud/drivers/dreamhost.py
@@ -33,202 +33,202 @@ currently support extensions to its interface, so we'll put some basic sizes
 in for node creation.
 """
 DH_PS_SIZES = {
-	'minimum': {
-		'id' : 'minimum',
-		'name' : 'Minimum DH PS size',
-		'ram' : 300,
-		'price' : 15,
-		'disk' : None,
-		'bandwidth' : None
-	},
-	'maximum': {
-		'id' : 'maximum',
-		'name' : 'Maximum DH PS size',
-		'ram' : 4000,
-		'price' : 200,
-		'disk' : None,
-		'bandwidth' : None
-	},
-	'default': {
-		'id' : 'default',
-		'name' : 'Default DH PS size',
-		'ram' : 2300,
-		'price' : 115,
-		'disk' : None,
-		'bandwidth' : None
-	},
-	'low': {
-		'id' : 'low',
-		'name' : 'DH PS with 1GB RAM',
-		'ram' : 1000,
-		'price' : 50,
-		'disk' : None,
-		'bandwidth' : None
-	},
-	'high': {
-		'id' : 'high',
-		'name' : 'DH PS with 3GB RAM',
-		'ram' : 3000,
-		'price' : 150,
-		'disk' : None,
-		'bandwidth' : None
-	},
+    'minimum': {
+        'id' : 'minimum',
+        'name' : 'Minimum DH PS size',
+        'ram' : 300,
+        'price' : 15,
+        'disk' : None,
+        'bandwidth' : None
+    },
+    'maximum': {
+        'id' : 'maximum',
+        'name' : 'Maximum DH PS size',
+        'ram' : 4000,
+        'price' : 200,
+        'disk' : None,
+        'bandwidth' : None
+    },
+    'default': {
+        'id' : 'default',
+        'name' : 'Default DH PS size',
+        'ram' : 2300,
+        'price' : 115,
+        'disk' : None,
+        'bandwidth' : None
+    },
+    'low': {
+        'id' : 'low',
+        'name' : 'DH PS with 1GB RAM',
+        'ram' : 1000,
+        'price' : 50,
+        'disk' : None,
+        'bandwidth' : None
+    },
+    'high': {
+        'id' : 'high',
+        'name' : 'DH PS with 3GB RAM',
+        'ram' : 3000,
+        'price' : 150,
+        'disk' : None,
+        'bandwidth' : None
+    },
 }
 
 class DreamhostAPIException(Exception):
-	def __str__(self):
-		return self.args[0]
-	def __repr__(self):
-		return "<DreamhostException '%s'>" % (self.args[0])
+    def __str__(self):
+        return self.args[0]
+    def __repr__(self):
+        return "<DreamhostException '%s'>" % (self.args[0])
 
 class DreamhostResponse(Response):
-	"""
-	Response class for DreamHost PS
-	"""
-
-	def parse_body(self):
-		resp = json.loads(self.body)
-		if resp['result'] != 'success':
-			raise Exception(self.api_parse_error(resp))
-		return resp['data']
-
-	def parse_error(self):
-		raise Exception
-
-	def api_parse_error(self, response):
-		if response.has_key('data'):
-			if response['data'] == 'invalid_api_key':
-				raise InvalidCredsException, "Oops!  You've entered an invalid API key"
-			else:
-				raise DreamhostAPIException(response['data'])
-		else:
-			raise DreamhostAPIException("Unknown problem: %s" % (self.body))
+    """
+    Response class for DreamHost PS
+    """
+
+    def parse_body(self):
+        resp = json.loads(self.body)
+        if resp['result'] != 'success':
+            raise Exception(self.api_parse_error(resp))
+        return resp['data']
+
+    def parse_error(self):
+        raise Exception
+
+    def api_parse_error(self, response):
+        if response.has_key('data'):
+            if response['data'] == 'invalid_api_key':
+                raise InvalidCredsException, "Oops!  You've entered an invalid API key"
+            else:
+                raise DreamhostAPIException(response['data'])
+        else:
+            raise DreamhostAPIException("Unknown problem: %s" % (self.body))
 
 class DreamhostConnection(ConnectionKey):
-	"""
-	Connection class to connect to DreamHost's API servers
-	"""
+    """
+    Connection class to connect to DreamHost's API servers
+    """
 
-	host = 'api.dreamhost.com'
-	responseCls = DreamhostResponse
-	format = 'json'
+    host = 'api.dreamhost.com'
+    responseCls = DreamhostResponse
+    format = 'json'
 
-	def add_default_params(self, params):
-		"""
-		Add key and format parameters to the request.  Eventually should add
-		unique_id to prevent re-execution of a single request.
-		"""
-		params['key'] = self.key
-		params['format'] = self.format
-		#params['unique_id'] = generate_unique_id()
-		return params
+    def add_default_params(self, params):
+        """
+        Add key and format parameters to the request.  Eventually should add
+        unique_id to prevent re-execution of a single request.
+        """
+        params['key'] = self.key
+        params['format'] = self.format
+        #params['unique_id'] = generate_unique_id()
+        return params
 
 
 class DreamhostNodeDriver(NodeDriver):
-	"""
-	Node Driver for DreamHost PS
-	"""
-	type = Provider.DREAMHOST
-	name = "Dreamhost"
-	connectionCls = DreamhostConnection
-	_sizes = DH_PS_SIZES
-
-	def create_node(self, **kwargs):
-		size = kwargs['size'].ram
-		params = {
-			'cmd' : 'dreamhost_ps-add_ps',
-			'movedata' : kwargs['movedata'],
-			'type' : kwargs['image'].name,
-			'size' : size
-		}
-		data = self.connection.request('/', params).object
-		return Node(
-			id = data['added_' + kwargs['image'].name],
-			name = data['added_' + kwargs['image'].name],
-			state = NodeState.PENDING,
-			public_ip = [],
-			private_ip = [],
-			driver = self.connection.driver,
-			extra = {
-				'type' : kwargs['image'].name
-			}
-		)
-
-	def destroy_node(self, node):
-		params = {
-			'cmd' : 'dreamhost_ps-remove_ps',
-			'ps' : node.id
-		}
-		try:
-			return self.connection.request('/', params).success()
-		except DreamhostAPIException:
-			return False
-
-	def reboot_node(self, node):
-		params = {
-			'cmd' : 'dreamhost_ps-reboot',
-			'ps' : node.id
-		}
-		try:
-			return self.connection.request('/', params).success()
-		except DreamhostAPIException:
-			return False
-
-	def list_nodes(self, **kwargs):
-		data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_ps' }).object
-		return [self._to_node(n) for n in data]
-
-	def list_images(self, **kwargs):
-		data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_images' }).object
-		images = []
-		for img in data:
-			images.append(NodeImage(
-				id = img['image'],
-				name = img['image'],
-				driver = self.connection.driver
-			))
-		return images
-
-	def list_sizes(self, **kwargs):
-		return [ NodeSize(driver=self.connection.driver, **i)
-			for i in self._sizes.values() ]
-
-	def list_locations(self, **kwargs):
-		raise NotImplementedError, \
-			'You cannot select a location for DreamHost Private Servers at this time.'
-
-	############################################
-	# Private Methods (helpers and extensions) #
-	############################################
-	def _resize_node(self, node, size):
-		if (size < 300 or size > 4000):
-			return False
-
-		params = {
-			'cmd' : 'dreamhost_ps-set_size',
-			'ps' : node.id,
-			'size' : size
-		}
-		try:
-			return self.connection.request('/', params).success()
-		except DreamhostAPIException:
-			return False
-
-	def _to_node(self, data):
-		"""
-		Convert the data from a DreamhostResponse object into a Node
-		"""
-		return Node(
-			id = data['ps'],
-			name = data['ps'],
-			state = NodeState.UNKNOWN,
-			public_ip = [data['ip']],
-			private_ip = [],
-			driver = self.connection.driver,
-			extra = {
-				'current_size' : data['memory_mb'],
-				'account_id' : data['account_id'],
-				'type' : data['type']
-			}
-		)
+    """
+    Node Driver for DreamHost PS
+    """
+    type = Provider.DREAMHOST
+    name = "Dreamhost"
+    connectionCls = DreamhostConnection
+    _sizes = DH_PS_SIZES
+
+    def create_node(self, **kwargs):
+        size = kwargs['size'].ram
+        params = {
+            'cmd' : 'dreamhost_ps-add_ps',
+            'movedata' : kwargs['movedata'],
+            'type' : kwargs['image'].name,
+            'size' : size
+        }
+        data = self.connection.request('/', params).object
+        return Node(
+            id = data['added_' + kwargs['image'].name],
+            name = data['added_' + kwargs['image'].name],
+            state = NodeState.PENDING,
+            public_ip = [],
+            private_ip = [],
+            driver = self.connection.driver,
+            extra = {
+                'type' : kwargs['image'].name
+            }
+        )
+
+    def destroy_node(self, node):
+        params = {
+            'cmd' : 'dreamhost_ps-remove_ps',
+            'ps' : node.id
+        }
+        try:
+            return self.connection.request('/', params).success()
+        except DreamhostAPIException:
+            return False
+
+    def reboot_node(self, node):
+        params = {
+            'cmd' : 'dreamhost_ps-reboot',
+            'ps' : node.id
+        }
+        try:
+            return self.connection.request('/', params).success()
+        except DreamhostAPIException:
+            return False
+
+    def list_nodes(self, **kwargs):
+        data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_ps' }).object
+        return [self._to_node(n) for n in data]
+
+    def list_images(self, **kwargs):
+        data = self.connection.request('/', { 'cmd' : 'dreamhost_ps-list_images' }).object
+        images = []
+        for img in data:
+            images.append(NodeImage(
+                id = img['image'],
+                name = img['image'],
+                driver = self.connection.driver
+            ))
+        return images
+
+    def list_sizes(self, **kwargs):
+        return [ NodeSize(driver=self.connection.driver, **i)
+            for i in self._sizes.values() ]
+
+    def list_locations(self, **kwargs):
+        raise NotImplementedError, \
+            'You cannot select a location for DreamHost Private Servers at this time.'
+
+    ############################################
+    # Private Methods (helpers and extensions) #
+    ############################################
+    def _resize_node(self, node, size):
+        if (size < 300 or size > 4000):
+            return False
+
+        params = {
+            'cmd' : 'dreamhost_ps-set_size',
+            'ps' : node.id,
+            'size' : size
+        }
+        try:
+            return self.connection.request('/', params).success()
+        except DreamhostAPIException:
+            return False
+
+    def _to_node(self, data):
+        """
+        Convert the data from a DreamhostResponse object into a Node
+        """
+        return Node(
+            id = data['ps'],
+            name = data['ps'],
+            state = NodeState.UNKNOWN,
+            public_ip = [data['ip']],
+            private_ip = [],
+            driver = self.connection.driver,
+            extra = {
+                'current_size' : data['memory_mb'],
+                'account_id' : data['account_id'],
+                'type' : data['type']
+            }
+        )
 
diff --git a/test/test_dreamhost.py b/test/test_dreamhost.py
index cb3e73c..8149eb2 100644
--- a/test/test_dreamhost.py
+++ b/test/test_dreamhost.py
@@ -30,246 +30,246 @@ from secrets import DREAMHOST_KEY
 #class DreamhostTest(unittest.TestCase, TestCaseMixin):
 class DreamhostTest(unittest.TestCase):
 
-	def setUp(self):
-		DreamhostNodeDriver.connectionCls.conn_classes = (
-			None,
-			DreamhostMockHttp
-		)
-		DreamhostMockHttp.type = None
-		DreamhostMockHttp.use_param = 'cmd'
-		self.driver = DreamhostNodeDriver('foo')
-
-	def test_invalid_creds(self):
-		"""
-		Tests the error-handling for passing a bad API Key to the DreamHost API
-		"""
-		DreamhostMockHttp.type = 'BAD_AUTH'
-		try:
-			self.driver.list_nodes()
-			self.assertTrue(False) # Above command should have thrown an InvalidCredsException
-		except InvalidCredsException:
-			self.assertTrue(True)
-
-
-	def test_list_nodes(self):
-		"""
-		Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
-			-	account_id: 000000
-				ip: 75.119.203.51
-				memory_mb: 500
-				ps: ps22174
-				start_date: 2010-02-25
-				type: web
-			-	account_id: 000000
-				ip: 75.119.203.52
-				memory_mb: 1500
-				ps: ps22175
-				start_date: 2010-02-25
-				type: mysql
-		"""
-
-		nodes = self.driver.list_nodes()
-		self.assertEqual(len(nodes), 2)
-		web_node = nodes[0]
-		mysql_node = nodes[1]
-
-		# Web node tests
-		self.assertEqual(web_node.id, 'ps22174')
-		self.assertEqual(web_node.state, NodeState.UNKNOWN)
-		self.assertTrue('75.119.203.51' in web_node.public_ip)
-		self.assertTrue(
-			web_node.extra.has_key('current_size') and
-			web_node.extra['current_size'] == 500
-		)
-		self.assertTrue(
-			web_node.extra.has_key('account_id') and
-			web_node.extra['account_id'] == 000000
-		)
-		self.assertTrue(
-			web_node.extra.has_key('type') and
-			web_node.extra['type'] == 'web'
-		)
-		# MySql node tests
-		self.assertEqual(mysql_node.id, 'ps22175')
-		self.assertEqual(mysql_node.state, NodeState.UNKNOWN)
-		self.assertTrue('75.119.203.52' in mysql_node.public_ip)
-		self.assertTrue(
-			mysql_node.extra.has_key('current_size') and
-			mysql_node.extra['current_size'] == 1500
-		)
-		self.assertTrue(
-			mysql_node.extra.has_key('account_id') and
-			mysql_node.extra['account_id'] == 000000
-		)
-		self.assertTrue(
-			mysql_node.extra.has_key('type') and
-			mysql_node.extra['type'] == 'mysql'
-		)
-
-	def test_create_node(self):
-		"""
-		Test create_node for DreamHost PS driver.
-		This is not remarkably compatible with libcloud.  The DH API allows
-		users to specify what image they want to create and whether to move
-		all their data to the (web) PS. It does NOT accept a name, size, or
-		location.  The only information it returns is the PS's context id
-		Once the PS is ready it will appear in the list generated by list_ps.
-		"""
-		new_node = self.driver.create_node(
-			image = self.driver.list_images()[0],
-			size = self.driver.list_sizes()[0],
-			movedata = 'no',
-		)
-		self.assertEqual(new_node.id, 'ps12345')
-		self.assertEqual(new_node.state, NodeState.PENDING)
-		self.assertTrue(
-			new_node.extra.has_key('type') and
-			new_node.extra['type'] == 'web'
-		)
-
-	def test_destroy_node(self):
-		"""
-		Test destroy_node for DreamHost PS driver
-		"""
-		node = self.driver.list_nodes()[0]
-		self.assertTrue(self.driver.destroy_node(node))
-
-	def test_destroy_node_failure(self):
-		"""
-		Test destroy_node failure for DreamHost PS driver
-		"""
-		node = self.driver.list_nodes()[0]
-
-		DreamhostMockHttp.type = 'API_FAILURE'
-		self.assertFalse(self.driver.destroy_node(node))
-
-	def test_reboot_node(self):
-		"""
-		Test reboot_node for DreamHost PS driver.
-		"""
-		node = self.driver.list_nodes()[0]
-		self.assertTrue(self.driver.reboot_node(node))
-
-	def test_reboot_node_failure(self):
-		"""
-		Test reboot_node failure for DreamHost PS driver
-		"""
-		node = self.driver.list_nodes()[0]
-
-		DreamhostMockHttp.type = 'API_FAILURE'
-		self.assertFalse(self.driver.reboot_node(node))
-
-	def test_resize_node(self):
-		"""
-		Test resize_node for DreamHost PS driver
-		"""
-		node = self.driver.list_nodes()[0]
-		self.assertTrue(self.driver._resize_node(node, 400))
-
-	def test_resize_node_failure(self):
-		"""
-		Test reboot_node faliure for DreamHost PS driver
-		"""
-		node = self.driver.list_nodes()[0]
-
-		DreamhostMockHttp.type = 'API_FAILURE'
-		self.assertFalse(self.driver._resize_node(node, 400))
-
-	def test_list_images(self):
-		"""
-		Test list_images for DreamHost PS driver.
-		"""
-		images = self.driver.list_images()
-		self.assertEqual(len(images), 2)
-		self.assertEqual(images[0].id, 'web')
-		self.assertEqual(images[0].name, 'web')
-		self.assertEqual(images[1].id, 'mysql')
-		self.assertEqual(images[1].name, 'mysql')
-
-	def test_list_sizes(self):
-		sizes = self.driver.list_sizes()
-		self.assertEqual(len(sizes), 5)
-
-		self.assertEqual(sizes[0].id, 'default')
-		self.assertEqual(sizes[0].bandwidth, None)
-		self.assertEqual(sizes[0].disk, None)
-		self.assertEqual(sizes[0].ram, 2300)
-		self.assertEqual(sizes[0].price, 115)
-
-	def test_list_locations(self):
-		try:
-			self.driver.list_locations()
-		except NotImplementedError:
-			pass
-	
+    def setUp(self):
+        DreamhostNodeDriver.connectionCls.conn_classes = (
+            None,
+            DreamhostMockHttp
+        )
+        DreamhostMockHttp.type = None
+        DreamhostMockHttp.use_param = 'cmd'
+        self.driver = DreamhostNodeDriver('foo')
+
+    def test_invalid_creds(self):
+        """
+        Tests the error-handling for passing a bad API Key to the DreamHost API
+        """
+        DreamhostMockHttp.type = 'BAD_AUTH'
+        try:
+            self.driver.list_nodes()
+            self.assertTrue(False) # Above command should have thrown an InvalidCredsException
+        except InvalidCredsException:
+            self.assertTrue(True)
+
+
+    def test_list_nodes(self):
+        """
+        Test list_nodes for DreamHost PS driver.  Should return a list of two nodes:
+            -   account_id: 000000
+                ip: 75.119.203.51
+                memory_mb: 500
+                ps: ps22174
+                start_date: 2010-02-25
+                type: web
+            -   account_id: 000000
+                ip: 75.119.203.52
+                memory_mb: 1500
+                ps: ps22175
+                start_date: 2010-02-25
+                type: mysql
+        """
+
+        nodes = self.driver.list_nodes()
+        self.assertEqual(len(nodes), 2)
+        web_node = nodes[0]
+        mysql_node = nodes[1]
+
+        # Web node tests
+        self.assertEqual(web_node.id, 'ps22174')
+        self.assertEqual(web_node.state, NodeState.UNKNOWN)
+        self.assertTrue('75.119.203.51' in web_node.public_ip)
+        self.assertTrue(
+            web_node.extra.has_key('current_size') and
+            web_node.extra['current_size'] == 500
+        )
+        self.assertTrue(
+            web_node.extra.has_key('account_id') and
+            web_node.extra['account_id'] == 000000
+        )
+        self.assertTrue(
+            web_node.extra.has_key('type') and
+            web_node.extra['type'] == 'web'
+        )
+        # MySql node tests
+        self.assertEqual(mysql_node.id, 'ps22175')
+        self.assertEqual(mysql_node.state, NodeState.UNKNOWN)
+        self.assertTrue('75.119.203.52' in mysql_node.public_ip)
+        self.assertTrue(
+            mysql_node.extra.has_key('current_size') and
+            mysql_node.extra['current_size'] == 1500
+        )
+        self.assertTrue(
+            mysql_node.extra.has_key('account_id') and
+            mysql_node.extra['account_id'] == 000000
+        )
+        self.assertTrue(
+            mysql_node.extra.has_key('type') and
+            mysql_node.extra['type'] == 'mysql'
+        )
+
+    def test_create_node(self):
+        """
+        Test create_node for DreamHost PS driver.
+        This is not remarkably compatible with libcloud.  The DH API allows
+        users to specify what image they want to create and whether to move
+        all their data to the (web) PS. It does NOT accept a name, size, or
+        location.  The only information it returns is the PS's context id
+        Once the PS is ready it will appear in the list generated by list_ps.
+        """
+        new_node = self.driver.create_node(
+            image = self.driver.list_images()[0],
+            size = self.driver.list_sizes()[0],
+            movedata = 'no',
+        )
+        self.assertEqual(new_node.id, 'ps12345')
+        self.assertEqual(new_node.state, NodeState.PENDING)
+        self.assertTrue(
+            new_node.extra.has_key('type') and
+            new_node.extra['type'] == 'web'
+        )
+
+    def test_destroy_node(self):
+        """
+        Test destroy_node for DreamHost PS driver
+        """
+        node = self.driver.list_nodes()[0]
+        self.assertTrue(self.driver.destroy_node(node))
+
+    def test_destroy_node_failure(self):
+        """
+        Test destroy_node failure for DreamHost PS driver
+        """
+        node = self.driver.list_nodes()[0]
+
+        DreamhostMockHttp.type = 'API_FAILURE'
+        self.assertFalse(self.driver.destroy_node(node))
+
+    def test_reboot_node(self):
+        """
+        Test reboot_node for DreamHost PS driver.
+        """
+        node = self.driver.list_nodes()[0]
+        self.assertTrue(self.driver.reboot_node(node))
+
+    def test_reboot_node_failure(self):
+        """
+        Test reboot_node failure for DreamHost PS driver
+        """
+        node = self.driver.list_nodes()[0]
+
+        DreamhostMockHttp.type = 'API_FAILURE'
+        self.assertFalse(self.driver.reboot_node(node))
+
+    def test_resize_node(self):
+        """
+        Test resize_node for DreamHost PS driver
+        """
+        node = self.driver.list_nodes()[0]
+        self.assertTrue(self.driver._resize_node(node, 400))
+
+    def test_resize_node_failure(self):
+        """
+        Test reboot_node faliure for DreamHost PS driver
+        """
+        node = self.driver.list_nodes()[0]
+
+        DreamhostMockHttp.type = 'API_FAILURE'
+        self.assertFalse(self.driver._resize_node(node, 400))
+
+    def test_list_images(self):
+        """
+        Test list_images for DreamHost PS driver.
+        """
+        images = self.driver.list_images()
+        self.assertEqual(len(images), 2)
+        self.assertEqual(images[0].id, 'web')
+        self.assertEqual(images[0].name, 'web')
+        self.assertEqual(images[1].id, 'mysql')
+        self.assertEqual(images[1].name, 'mysql')
+
+    def test_list_sizes(self):
+        sizes = self.driver.list_sizes()
+        self.assertEqual(len(sizes), 5)
+
+        self.assertEqual(sizes[0].id, 'default')
+        self.assertEqual(sizes[0].bandwidth, None)
+        self.assertEqual(sizes[0].disk, None)
+        self.assertEqual(sizes[0].ram, 2300)
+        self.assertEqual(sizes[0].price, 115)
+
+    def test_list_locations(self):
+        try:
+            self.driver.list_locations()
+        except NotImplementedError:
+            pass
+
 class DreamhostMockHttp(MockHttp):
 
-	def _BAD_AUTH_dreamhost_ps_list_ps(self, method, url, body, headers):
-		body = json.dumps({'data' : 'invalid_api_key', 'result' : 'error'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_add_ps(self, method, url, body, headers):
-		body = json.dumps({'data' : {'added_web' : 'ps12345'}, 'result' : 'success'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_list_ps(self, method, url, body, headers):
-		data = [{
-			'account_id' : 000000,
-			'ip': '75.119.203.51',
-			'memory_mb' : 500,
-			'ps' : 'ps22174',
-			'start_date' : '2010-02-25',
-			'type' : 'web'
-		},
-		{
-			'account_id' : 000000,
-			'ip' : '75.119.203.52',
-			'memory_mb' : 1500,
-			'ps' : 'ps22175',
-			'start_date' : '2010-02-25',
-			'type' : 'mysql'
-		}]
-		result = 'success'
-		body = json.dumps({'data' : data, 'result' : result})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_list_images(self, method, url, body, headers):
-		data = [{
-			'description' : 'Private web server',
-			'image' : 'web'
-		},
-		{
-			'description' : 'Private MySQL server',
-			'image' : 'mysql'
-		}]
-		result = 'success'
-		body = json.dumps({'data' : data, 'result' : result})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_reboot(self, method, url, body, headers):
-		body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _API_FAILURE_dreamhost_ps_reboot(self, method, url, body, headers):
-		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_set_size(self, method, url, body, headers):
-		body = json.dumps({'data' : {'memory-mb' : '500'}, 'result' : 'success'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _API_FAILURE_dreamhost_ps_set_size(self, method, url, body, headers):
-		body = json.dumps({'data' : 'internal_error_setting_size', 'result' : 'error'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _dreamhost_ps_remove_ps(self, method, url, body, headers):
-		body = json.dumps({'data' : 'removed_web', 'result' : 'success'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-
-	def _API_FAILURE_dreamhost_ps_remove_ps(self, method, url, body, headers):
-		body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
-		return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+    def _BAD_AUTH_dreamhost_ps_list_ps(self, method, url, body, headers):
+        body = json.dumps({'data' : 'invalid_api_key', 'result' : 'error'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_add_ps(self, method, url, body, headers):
+        body = json.dumps({'data' : {'added_web' : 'ps12345'}, 'result' : 'success'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_list_ps(self, method, url, body, headers):
+        data = [{
+            'account_id' : 000000,
+            'ip': '75.119.203.51',
+            'memory_mb' : 500,
+            'ps' : 'ps22174',
+            'start_date' : '2010-02-25',
+            'type' : 'web'
+        },
+        {
+            'account_id' : 000000,
+            'ip' : '75.119.203.52',
+            'memory_mb' : 1500,
+            'ps' : 'ps22175',
+            'start_date' : '2010-02-25',
+            'type' : 'mysql'
+        }]
+        result = 'success'
+        body = json.dumps({'data' : data, 'result' : result})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_list_images(self, method, url, body, headers):
+        data = [{
+            'description' : 'Private web server',
+            'image' : 'web'
+        },
+        {
+            'description' : 'Private MySQL server',
+            'image' : 'mysql'
+        }]
+        result = 'success'
+        body = json.dumps({'data' : data, 'result' : result})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_reboot(self, method, url, body, headers):
+        body = json.dumps({'data' : 'reboot_scheduled', 'result' : 'success'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _API_FAILURE_dreamhost_ps_reboot(self, method, url, body, headers):
+        body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_set_size(self, method, url, body, headers):
+        body = json.dumps({'data' : {'memory-mb' : '500'}, 'result' : 'success'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _API_FAILURE_dreamhost_ps_set_size(self, method, url, body, headers):
+        body = json.dumps({'data' : 'internal_error_setting_size', 'result' : 'error'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _dreamhost_ps_remove_ps(self, method, url, body, headers):
+        body = json.dumps({'data' : 'removed_web', 'result' : 'success'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _API_FAILURE_dreamhost_ps_remove_ps(self, method, url, body, headers):
+        body = json.dumps({'data' : 'no_such_ps', 'result' : 'error'})
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
 if __name__ == '__main__':
-	sys.exit(unittest.main())
+    sys.exit(unittest.main())
 
-- 
1.5.4.1

