From bb3cb2e0684d268b73771c97fde7d3d828a08bac Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 09:49:18 -0500
Subject: [PATCH 01/32] Commit with initial functionality.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b8f37fd..e027521 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -280,6 +280,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_destroy_balancers(self, *balancers):
+        ids = [("id", balancer.id) for balancer in balancers]
+        resp = self.connection.request('/loadbalancers',
+            method='DELETE',
+            params=ids)
+
+        return resp.status == httplib.ACCEPTED
+
     def get_balancer(self, balancer_id):
         uri = '/loadbalancers/%s' % (balancer_id)
         resp = self.connection.request(uri)
@@ -310,6 +318,13 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_detach_members(self, balancer, members):
+        uri = '/loadbalancers/%s/nodes' % (balancer.id)
+        ids = [("id", member.id) for member in members]
+        resp = self.connection.request(uri, method='DELETE', params=ids)
+
+        return resp.status == httplib.ACCEPTED
+
     def balancer_list_members(self, balancer):
         uri = '/loadbalancers/%s/nodes' % (balancer.id)
         return self._to_members(
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 66641e0..adac753 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -23,6 +23,7 @@ except ImportError:
     import json
 
 from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urllib
 
 from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm
 from libcloud.loadbalancer.types import MemberCondition
@@ -108,6 +109,11 @@ class RackspaceLBTests(unittest.TestCase):
         ret = self.driver.destroy_balancer(balancer)
         self.assertTrue(ret)
 
+    def test_ex_destroy_balancers(self):
+        balancers = self.driver.list_balancers()
+        ret = self.driver.ex_destroy_balancers(*balancers)
+        self.assertTrue(ret)
+
     def test_get_balancer(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
 
@@ -308,6 +314,13 @@ class RackspaceLBTests(unittest.TestCase):
         ret = balancer.detach_member(member)
         self.assertTrue(ret)
 
+    def test_ex_detach_members(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        members = balancer.list_members()
+
+        ret = self.driver.ex_balancer_detach_members(balancer, members)
+        self.assertTrue(ret)
+
     def test_update_balancer_protocol(self):
         balancer = LoadBalancer(id='3130', name='LB_update',
                                          state='PENDING_UPDATE', ip='10.34.4.3',
@@ -442,6 +455,16 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             body = self.fixtures.load('v1_slug_loadbalancers_post.json')
             return (httplib.ACCEPTED, body, {},
                     httplib.responses[httplib.ACCEPTED])
+        elif method == "DELETE":
+            balancers = self.fixtures.load('v1_slug_loadbalancers.json')
+            balancers_json = json.loads(balancers)
+
+            for balancer in balancers_json["loadBalancers"]:
+                id = balancer["id"]
+                self.assertTrue(urllib.urlencode([("id", id)]) in url,
+                    msg="Did not delete balancer with id %d" % id)
+
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
@@ -456,8 +479,11 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_8290(self, method, url, body, headers):
-        body = self.fixtures.load('v1_slug_loadbalancers_8290.json')
-        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+        if method == "GET":
+            body = self.fixtures.load('v1_slug_loadbalancers_8290.json')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_8290_nodes(self, method, url, body, headers):
         if method == "GET":
@@ -467,6 +493,16 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             body = self.fixtures.load('v1_slug_loadbalancers_8290_nodes_post.json')
             return (httplib.ACCEPTED, body, {},
                     httplib.responses[httplib.ACCEPTED])
+        elif method == "DELETE":
+            nodes = self.fixtures.load('v1_slug_loadbalancers_8290_nodes.json')
+            json_nodes = json.loads(nodes)
+
+            for node in json_nodes["nodes"]:
+                id = node["id"]
+                self.assertTrue(urllib.urlencode([("id", id)]) in url,
+                    msg="Did not delete member with id %d" % id)
+
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
-- 
1.7.5.4


From 41a21d0f3abc79def3930ad28bcc4a1b29b9d00a Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 09:49:59 -0500
Subject: [PATCH 02/32] Don't use *args, just pass in a list.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index e027521..affa23b 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -280,7 +280,7 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_destroy_balancers(self, *balancers):
+    def ex_destroy_balancers(self, balancers):
         ids = [("id", balancer.id) for balancer in balancers]
         resp = self.connection.request('/loadbalancers',
             method='DELETE',
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index adac753..8bb593b 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -111,7 +111,7 @@ class RackspaceLBTests(unittest.TestCase):
 
     def test_ex_destroy_balancers(self):
         balancers = self.driver.list_balancers()
-        ret = self.driver.ex_destroy_balancers(*balancers)
+        ret = self.driver.ex_destroy_balancers(balancers)
         self.assertTrue(ret)
 
     def test_get_balancer(self):
-- 
1.7.5.4


From c27ccfc5be3e5defa49aa19e7f59eb4709a3ec2e Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 09:55:47 -0500
Subject: [PATCH 03/32] Replace double quotes with single quotes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index affa23b..92ccf73 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -281,7 +281,7 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_destroy_balancers(self, balancers):
-        ids = [("id", balancer.id) for balancer in balancers]
+        ids = [('id', balancer.id) for balancer in balancers]
         resp = self.connection.request('/loadbalancers',
             method='DELETE',
             params=ids)
@@ -320,7 +320,7 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_detach_members(self, balancer, members):
         uri = '/loadbalancers/%s/nodes' % (balancer.id)
-        ids = [("id", member.id) for member in members]
+        ids = [('id', member.id) for member in members]
         resp = self.connection.request(uri, method='DELETE', params=ids)
 
         return resp.status == httplib.ACCEPTED
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 8bb593b..68c7c48 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -459,12 +459,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             balancers = self.fixtures.load('v1_slug_loadbalancers.json')
             balancers_json = json.loads(balancers)
 
-            for balancer in balancers_json["loadBalancers"]:
-                id = balancer["id"]
-                self.assertTrue(urllib.urlencode([("id", id)]) in url,
-                    msg="Did not delete balancer with id %d" % id)
+            for balancer in balancers_json['loadBalancers']:
+                id = balancer['id']
+                self.assertTrue(urllib.urlencode([('id', id)]) in url,
+                    msg='Did not delete balancer with id %d' % id)
 
-            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
@@ -497,12 +497,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             nodes = self.fixtures.load('v1_slug_loadbalancers_8290_nodes.json')
             json_nodes = json.loads(nodes)
 
-            for node in json_nodes["nodes"]:
-                id = node["id"]
-                self.assertTrue(urllib.urlencode([("id", id)]) in url,
-                    msg="Did not delete member with id %d" % id)
+            for node in json_nodes['nodes']:
+                id = node['id']
+                self.assertTrue(urllib.urlencode([('id', id)]) in url,
+                    msg='Did not delete member with id %d' % id)
 
-            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
-- 
1.7.5.4


From 738635e23223503b12da52615d46493e5e0032bc Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 10:19:45 -0500
Subject: [PATCH 04/32] Add fixture for node.


 create mode 100644 test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json

diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json
new file mode 100644
index 0000000..8d6fb42
--- /dev/null
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json
@@ -0,0 +1 @@
+{"node":{"address":"10.1.0.11","id":30944,"port":80,"status":"ONLINE","condition":"ENABLED","weight":12}}
\ No newline at end of file
-- 
1.7.5.4


From d64ece2157f09eaa64337aa9e3a432809e63e807 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 11:13:52 -0500
Subject: [PATCH 05/32] Add ex_balancer_update_member and
 ex_balancer_update_member_no_poll.


 delete mode 100644 test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json

diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b8f37fd..80a7e89 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -223,6 +223,8 @@ class RackspaceLBDriver(Driver):
         'DRAINING': MemberCondition.DRAINING
     }
 
+    CONDITION_LB_MEMBER_MAP = reverse_dict(LB_MEMBER_CONDITION_MAP)
+
     _VALUE_TO_ALGORITHM_MAP = {
         'RANDOM': Algorithm.RANDOM,
         'ROUND_ROBIN': Algorithm.ROUND_ROBIN,
@@ -331,6 +333,42 @@ class RackspaceLBDriver(Driver):
                     data=json.dumps(attrs))
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_update_member(self, balancer, member, **kwargs):
+        resp = self.connection.request(
+            action='/loadbalancers/%s/nodes/%s' % (balancer.id, member.id),
+            method='PUT',
+            data=json.dumps(self._kwargs_to_mutable_member_attrs(**kwargs))
+        )
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError("Update member attributes was not accepted")
+
+        # Updating a member puts a balancer into "PENDING_UPDATE" status.
+        # Wait until the balancer is back in 'ACTIVE' status and fetch
+        # the updated member.
+        balancer_resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        balancer = self._to_balancer(balancer_resp.object["loadBalancer"])
+        extra_members = balancer.extra["members"]
+
+        updated_members = [extra_member for extra_member in extra_members \
+                           if extra_member.id == member.id]
+
+        if updated_members:
+            return updated_members[0]
+        return None
+
+    def ex_balancer_update_member_no_poll(self, balancer, member, **kwargs):
+        resp = self.connection.request(
+            action='/loadbalancers/%s/nodes/%s' % (balancer.id, member.id),
+            method='PUT',
+            data=json.dumps(self._kwargs_to_mutable_member_attrs(**kwargs))
+        )
+
+        return resp.status == httplib.ACCEPTED
+
     def ex_list_algorithm_names(self):
         """
         Lists algorithms supported by the API.  Returned as strings because
@@ -463,6 +501,16 @@ class RackspaceLBDriver(Driver):
 
         return update_attrs
 
+    def _kwargs_to_mutable_member_attrs(self, **attrs):
+        update_attrs = {}
+        if "condition" in attrs:
+            update_attrs["condition"] = self.CONDITION_LB_MEMBER_MAP.get(attrs["condition"])
+
+        if "weight" in attrs:
+            update_attrs["weight"] = attrs["weight"]
+
+        return update_attrs
+
     def _ex_private_virtual_ips(self, el):
         if not 'virtualIps' in el:
             return None
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json
deleted file mode 100644
index 8d6fb42..0000000
--- a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_nodes_30944.json
+++ /dev/null
@@ -1 +0,0 @@
-{"node":{"address":"10.1.0.11","id":30944,"port":80,"status":"ONLINE","condition":"ENABLED","weight":12}}
\ No newline at end of file
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 66641e0..b43f5e6 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -397,6 +397,29 @@ class RackspaceLBTests(unittest.TestCase):
         else:
             self.fail('Should have thrown exception with bad algorithm value')
 
+    def test_ex_update_balancer_member_extra_attributes(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        members = self.driver.balancer_list_members(balancer)
+
+        first_member = members[0]
+
+        member = self.driver.ex_balancer_update_member(balancer, first_member,
+            condition=MemberCondition.ENABLED, weight=12)
+
+        self.assertEquals(MemberCondition.ENABLED, member.extra["condition"])
+        self.assertEquals(12, member.extra["weight"])
+
+    def test_ex_update_balancer_member_no_poll_extra_attributes(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        members = self.driver.balancer_list_members(balancer)
+
+        first_member = members[0]
+
+        resp = self.driver.ex_balancer_update_member_no_poll(balancer, first_member,
+            condition=MemberCondition.ENABLED, weight=12)
+        self.assertTrue(resp)
+
+
 class RackspaceUKLBTests(RackspaceLBTests):
 
     def setUp(self):
@@ -471,7 +494,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_8290_nodes_30944(self, method, url, body, headers):
-        if method == "DELETE":
+        if method == "PUT":
+            json_body = json.loads(body)
+            self.assertEqual('ENABLED', json_body['condition'])
+            self.assertEqual(12, json_body['weight'])
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+        elif method == "DELETE":
             return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
-- 
1.7.5.4


From 0b2679a68c811ba9b10066aafc2598db8eb45076 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 11:16:02 -0500
Subject: [PATCH 06/32] Single quotes for double quotes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 80a7e89..c66bbf8 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -341,17 +341,17 @@ class RackspaceLBDriver(Driver):
         )
 
         if resp.status != httplib.ACCEPTED:
-            raise LibcloudError("Update member attributes was not accepted")
+            raise LibcloudError('Update member attributes was not accepted')
 
-        # Updating a member puts a balancer into "PENDING_UPDATE" status.
+        # Updating a member puts a balancer into 'PENDING_UPDATE' status.
         # Wait until the balancer is back in 'ACTIVE' status and fetch
         # the updated member.
         balancer_resp = self.connection.async_request(
             action='/loadbalancers/%s' % balancer.id,
             method='GET')
 
-        balancer = self._to_balancer(balancer_resp.object["loadBalancer"])
-        extra_members = balancer.extra["members"]
+        balancer = self._to_balancer(balancer_resp.object['loadBalancer'])
+        extra_members = balancer.extra['members']
 
         updated_members = [extra_member for extra_member in extra_members \
                            if extra_member.id == member.id]
@@ -503,11 +503,11 @@ class RackspaceLBDriver(Driver):
 
     def _kwargs_to_mutable_member_attrs(self, **attrs):
         update_attrs = {}
-        if "condition" in attrs:
-            update_attrs["condition"] = self.CONDITION_LB_MEMBER_MAP.get(attrs["condition"])
+        if 'condition' in attrs:
+            update_attrs['condition'] = self.CONDITION_LB_MEMBER_MAP.get(attrs['condition'])
 
-        if "weight" in attrs:
-            update_attrs["weight"] = attrs["weight"]
+        if 'weight' in attrs:
+            update_attrs['weight'] = attrs['weight']
 
         return update_attrs
 
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index b43f5e6..908a319 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -406,8 +406,8 @@ class RackspaceLBTests(unittest.TestCase):
         member = self.driver.ex_balancer_update_member(balancer, first_member,
             condition=MemberCondition.ENABLED, weight=12)
 
-        self.assertEquals(MemberCondition.ENABLED, member.extra["condition"])
-        self.assertEquals(12, member.extra["weight"])
+        self.assertEquals(MemberCondition.ENABLED, member.extra['condition'])
+        self.assertEquals(12, member.extra['weight'])
 
     def test_ex_update_balancer_member_no_poll_extra_attributes(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -498,7 +498,7 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             json_body = json.loads(body)
             self.assertEqual('ENABLED', json_body['condition'])
             self.assertEqual(12, json_body['weight'])
-            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
         elif method == "DELETE":
             return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
 
-- 
1.7.5.4


From 66daebdb4d2d05d777dbc56fe0aef7177a60b3c6 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Wed, 4 Jan 2012 11:29:17 -0500
Subject: [PATCH 07/32] Improve behavior when an updated member could not be
 found.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index c66bbf8..5f8c1e5 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -356,9 +356,10 @@ class RackspaceLBDriver(Driver):
         updated_members = [extra_member for extra_member in extra_members \
                            if extra_member.id == member.id]
 
-        if updated_members:
-            return updated_members[0]
-        return None
+        if not updated_members:
+            raise LibcloudError('Could not find updated member')
+
+        return updated_members[0]
 
     def ex_balancer_update_member_no_poll(self, balancer, member, **kwargs):
         resp = self.connection.request(
-- 
1.7.5.4


From bcb2de62105e13b1a137f23e10930820980bc250 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 08:55:02 -0500
Subject: [PATCH 08/32] Polling update health monitor.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b8f37fd..268b335 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -74,6 +74,20 @@ class RackspaceHealthMonitor(object):
         self.timeout = timeout
         self.attempts_before_deactivation = attempts_before_deactivation
 
+    def __repr__(self):
+        return ('<RackspaceHealthMonitor: type=%s, delay=%d, timeout=%d, '
+                'attempts_before_deactivation=%d' %
+                (self.type, self.delay, self.timeout,
+                 self.attempts_before_deactivation))
+
+    def to_dict(self):
+        return {
+            'type': self.type,
+            'delay': self.delay,
+            'timeout': self.timeout,
+            'attemptsBeforeDeactivation': self.attempts_before_deactivation
+        }
+
 
 class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor):
     """
@@ -99,6 +113,22 @@ class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor):
         self.body_regex = body_regex
         self.status_regex = status_regex
 
+    def __repr__(self):
+        return ('<RackspaceHTTPHealthMonitor: type=%s, delay=%d, timeout=%d, '
+                'attempts_before_deactivation=%d, path=%s, body_regex=%s, '
+                'status_regex=%s' %
+                (self.type, self.delay, self.timeout,
+                 self.attempts_before_deactivation, self.path, self.body_regex,
+                 self.status_regex))
+
+    def to_dict(self):
+        super_dict = super(RackspaceHTTPHealthMonitor, self).to_dict()
+        super_dict["path"] = self.path
+        super_dict["statusRegex"] = self.status_regex
+        super_dict["bodyRegex"] = self.body_regex
+
+        return super_dict
+
 
 class RackspaceConnectionThrottle(object):
     """
@@ -131,6 +161,13 @@ class RackspaceConnectionThrottle(object):
         self.max_connection_rate = max_connection_rate
         self.rate_interval_seconds = rate_interval_seconds
 
+    def __repr__(self):
+        return ('<RackspaceConnectionThrottle: min_connections=%d, '
+                'max_connections=%d, max_connection_rate=%d, '
+                'rate_interval_seconds=%d' %
+                (self.min_connections, self.max_connections,
+                 self.max_connection_rate, self.rate_interval_seconds))
+
 
 class RackspaceAccessRuleType(object):
     ALLOW = 0
@@ -351,6 +388,19 @@ class RackspaceLBDriver(Driver):
 
         return [self._to_access_rule(el) for el in resp.object["accessList"]]
 
+    def ex_balancer_update_health_monitor(self, balancer, health_monitor):
+        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
+
+        self.connection.request(uri,
+            method="PUT",
+            data=json.dumps(health_monitor.to_dict()))
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json
index 6d8dc16..68883b5 100644
--- a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json
@@ -1 +1 @@
-{"loadBalancer":{"name":"new ord balancer","id":18940,"protocol":"HTTP","port":80,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"OFFLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"CONNECT","delay":10,"timeout":5,"attemptsBeforeDeactivation":2},"sessionPersistence":{"persistenceType":"HTTP_COOKIE"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T16:01:36Z"},"connectionThrottle":{"maxConnections":200,"minConnections":50,"maxConnectionRate":50,"rateInterval":10},"connectionLogging":{"enabled":true}}}
\ No newline at end of file
+{"loadBalancer":{"name":"new ord balancer","id":94695,"protocol":"HTTP","port":80,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"OFFLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"CONNECT","delay":10,"timeout":5,"attemptsBeforeDeactivation":2},"sessionPersistence":{"persistenceType":"HTTP_COOKIE"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T16:01:36Z"},"connectionThrottle":{"maxConnections":200,"minConnections":50,"maxConnectionRate":50,"rateInterval":10},"connectionLogging":{"enabled":true}}}
\ No newline at end of file
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json
index 0bf9aba..ac26cb4 100644
--- a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json
@@ -1 +1 @@
-{"loadBalancer":{"name":"new ord balancer","id":18940,"protocol":"HTTP","port":80,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"ONLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"HTTP","path":"/","delay":10,"timeout":5,"attemptsBeforeDeactivation":2,"statusRegex":"^[234][0-9][0-9]$","bodyRegex":"Hello World!"},"sessionPersistence":{"persistenceType":"HTTP_COOKIE"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T16:51:32Z"},"connectionThrottle":{"maxConnections":100,"minConnections":25,"maxConnectionRate":25,"rateInterval":5},"connectionLogging":{"enabled":true}}}
\ No newline at end of file
+{"loadBalancer":{"name":"new ord balancer","id":94696,"protocol":"HTTP","port":80,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"ONLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"HTTP","path":"/","delay":10,"timeout":5,"attemptsBeforeDeactivation":2,"statusRegex":"^[234][0-9][0-9]$","bodyRegex":"Hello World!"},"sessionPersistence":{"persistenceType":"HTTP_COOKIE"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T16:51:32Z"},"connectionThrottle":{"maxConnections":100,"minConnections":25,"maxConnectionRate":25,"rateInterval":5},"connectionLogging":{"enabled":true}}}
\ No newline at end of file
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json
index 34e400a..eaac2f0 100644
--- a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json
@@ -1 +1 @@
-{"loadBalancer":{"name":"new ord balancer","id":18940,"protocol":"HTTPS","port":443,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"PENDING_UPDATE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"OFFLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"HTTPS","path":"/test","delay":15,"timeout":12,"attemptsBeforeDeactivation":5,"statusRegex":"^[234][0-9][0-9]$","bodyRegex":"abcdef"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T19:34:34Z"},"accessList":[{"address":"0.0.0.0/0","id":2883,"type":"DENY"},{"address":"2001:4801:7901::6/64","id":2884,"type":"ALLOW"}],"connectionThrottle":{"maxConnections":100,"minConnections":25,"maxConnectionRate":25,"rateInterval":5},"connectionLogging":{"enabled":true}}}
+{"loadBalancer":{"name":"new ord balancer","id":94697,"protocol":"HTTPS","port":443,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"PENDING_UPDATE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"OFFLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"HTTPS","path":"/test","delay":15,"timeout":12,"attemptsBeforeDeactivation":5,"statusRegex":"^[234][0-9][0-9]$","bodyRegex":"abcdef"},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2011-12-09T19:34:34Z"},"accessList":[{"address":"0.0.0.0/0","id":2883,"type":"DENY"},{"address":"2001:4801:7901::6/64","id":2884,"type":"ALLOW"}],"connectionThrottle":{"maxConnections":100,"minConnections":25,"maxConnectionRate":25,"rateInterval":5},"connectionLogging":{"enabled":true}}}
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 66641e0..5ba639c 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -26,7 +26,7 @@ from libcloud.utils.py3 import httplib
 
 from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm
 from libcloud.loadbalancer.types import MemberCondition
-from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver
+from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver, RackspaceHealthMonitor, RackspaceHTTPHealthMonitor
 from libcloud.loadbalancer.drivers.rackspace import RackspaceUKLBDriver
 from libcloud.loadbalancer.drivers.rackspace import RackspaceAccessRuleType
 from libcloud.common.types import LibcloudError
@@ -258,6 +258,38 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(allow_rule.address, "2001:4801:7901::6/64")
         self.assertEquals(allow_rule.rule_type, RackspaceAccessRuleType.ALLOW)
 
+    def test_balancer_update_health_monitor(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
+            attempts_before_deactivation=2)
+
+        balancer = self.driver.ex_balancer_update_health_monitor(balancer, monitor)
+        updated_monitor = balancer.extra['healthMonitor']
+
+        self.assertEquals('CONNECT', updated_monitor.type)
+        self.assertEquals(10, updated_monitor.delay)
+        self.assertEquals(5, updated_monitor.timeout)
+        self.assertEquals(2, updated_monitor.attempts_before_deactivation)
+
+    def test_balancer_update_http_health_monitor(self):
+        balancer = self.driver.get_balancer(balancer_id='94696')
+        monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
+            attempts_before_deactivation=2,
+            path='/',
+            status_regex='^[234][0-9][0-9]$',
+            body_regex='Hello World!')
+
+        balancer = self.driver.ex_balancer_update_health_monitor(balancer, monitor)
+        updated_monitor = balancer.extra['healthMonitor']
+
+        self.assertEquals('HTTP', updated_monitor.type)
+        self.assertEquals(10, updated_monitor.delay)
+        self.assertEquals(5, updated_monitor.timeout)
+        self.assertEquals(2, updated_monitor.attempts_before_deactivation)
+        self.assertEquals('/', updated_monitor.path)
+        self.assertEquals('^[234][0-9][0-9]$', updated_monitor.status_regex)
+        self.assertEquals('Hello World!', updated_monitor.body_regex)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -539,6 +571,19 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94695_healthmonitor(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertEquals('CONNECT', json_body['type'])
+            self.assertEquals(10, json_body['delay'])
+            self.assertEquals(5, json_body['timeout'])
+            self.assertEquals(2, json_body['attemptsBeforeDeactivation'])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json")
@@ -546,6 +591,22 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94696_healthmonitor(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertEquals('HTTP', json_body['type'])
+            self.assertEquals(10, json_body['delay'])
+            self.assertEquals(5, json_body['timeout'])
+            self.assertEquals(2, json_body['attemptsBeforeDeactivation'])
+            self.assertEquals('/', json_body['path'])
+            self.assertEquals('^[234][0-9][0-9]$', json_body['statusRegex'])
+            self.assertEquals('Hello World!', json_body['bodyRegex'])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94697(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94697_https_health_monitor.json")
-- 
1.7.5.4


From 0698eb06190cef46c8c4e4f290e592ce779856c7 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 10:20:39 -0500
Subject: [PATCH 09/32] Single quotes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 268b335..f5ab6d5 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -391,8 +391,8 @@ class RackspaceLBDriver(Driver):
     def ex_balancer_update_health_monitor(self, balancer, health_monitor):
         uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
 
-        self.connection.request(uri,
-            method="PUT",
+        resp = self.connection.request(uri,
+            method='PUT',
             data=json.dumps(health_monitor.to_dict()))
 
         resp = self.connection.async_request(
-- 
1.7.5.4


From 9bae87e67a01975ed80385b919af3e5e167e00c4 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 10:21:15 -0500
Subject: [PATCH 10/32] Update without polling and disable health monitor
 with/without polling.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index f5ab6d5..5c1c122 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -395,12 +395,48 @@ class RackspaceLBDriver(Driver):
             method='PUT',
             data=json.dumps(health_monitor.to_dict()))
 
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Update health monitor request not accepted')
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_balancer_update_health_monitor_no_poll(self, balancer,
+                                                  health_monitor):
+        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
+
+        resp = self.connection.request(uri,
+            method='PUT',
+            data=json.dumps(health_monitor.to_dict()))
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_balancer_disable_health_monitor(self, balancer):
+        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
+
+        resp = self.connection.request(uri,
+            method='DELETE')
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Disable health monitor request not accepted')
+
         resp = self.connection.async_request(
             action='/loadbalancers/%s' % balancer.id,
             method='GET')
 
         return self._to_balancer(resp.object['loadBalancer'])
 
+    def ex_balancer_disable_health_monitor_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
+
+        resp = self.connection.request(uri,
+            method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 5ba639c..6a4f3b3 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -290,6 +290,41 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals('^[234][0-9][0-9]$', updated_monitor.status_regex)
         self.assertEquals('Hello World!', updated_monitor.body_regex)
 
+    def test_balancer_update_health_monitor_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
+            attempts_before_deactivation=2)
+
+        resp = self.driver.ex_balancer_update_health_monitor_no_poll(balancer,
+            monitor)
+
+        self.assertTrue(resp)
+
+    def test_balancer_update_http_health_monitor_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94696')
+        monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
+            attempts_before_deactivation=2,
+            path='/',
+            status_regex='^[234][0-9][0-9]$',
+            body_regex='Hello World!')
+
+        resp = self.driver.ex_balancer_update_health_monitor_no_poll(balancer,
+            monitor)
+
+        self.assertTrue(resp)
+
+    def test_balancer_disable_health_monitor(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_balancer_disable_health_monitor(balancer)
+
+        self.assertTrue("healthMonitor" not in balancer.extra)
+
+    def test_balancer_disable_health_monitor_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        resp = self.driver.ex_balancer_disable_health_monitor_no_poll(balancer)
+
+        self.assertTrue(resp)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -508,6 +543,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_18940(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_18940_ex_public_ips.json")
-- 
1.7.5.4


From e96655c2eac23b19b4443647fccbe43b5b1966a8 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 10:54:44 -0500
Subject: [PATCH 11/32] Update and disable connection throttle with/without
 polling.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 5c1c122..dd8cb91 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -168,6 +168,14 @@ class RackspaceConnectionThrottle(object):
                 (self.min_connections, self.max_connections,
                  self.max_connection_rate, self.rate_interval_seconds))
 
+    def to_dict(self):
+        return {
+            'maxConnections': self.max_connections,
+            'minConnections': self.min_connections,
+            'maxConnectionRate': self.max_connection_rate,
+            'rateInterval' : self.rate_interval_seconds
+        }
+
 
 class RackspaceAccessRuleType(object):
     ALLOW = 0
@@ -437,6 +445,48 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_update_connection_throttle(self, balancer,
+                                               connection_throttle):
+        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps(connection_throttle.to_dict()))
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Update connection throttle request not accepted')
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_balancer_update_connection_throttle_no_poll(self, balancer,
+                                                       connection_throttle):
+        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps(connection_throttle.to_dict()))
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_balancer_disable_connection_throttle(self, balancer):
+        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Disable connection throttle request not accepted')
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_balancer_disable_connection_throttle_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 6a4f3b3..f233c59 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -26,7 +26,7 @@ from libcloud.utils.py3 import httplib
 
 from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm
 from libcloud.loadbalancer.types import MemberCondition
-from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver, RackspaceHealthMonitor, RackspaceHTTPHealthMonitor
+from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver, RackspaceHealthMonitor, RackspaceHTTPHealthMonitor, RackspaceConnectionThrottle
 from libcloud.loadbalancer.drivers.rackspace import RackspaceUKLBDriver
 from libcloud.loadbalancer.drivers.rackspace import RackspaceAccessRuleType
 from libcloud.common.types import LibcloudError
@@ -317,7 +317,7 @@ class RackspaceLBTests(unittest.TestCase):
         balancer = self.driver.get_balancer(balancer_id='8290')
         balancer = self.driver.ex_balancer_disable_health_monitor(balancer)
 
-        self.assertTrue("healthMonitor" not in balancer.extra)
+        self.assertTrue('healthMonitor' not in balancer.extra)
 
     def test_balancer_disable_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -325,6 +325,48 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_balancer_update_connection_throttle(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        connection_throttle = RackspaceConnectionThrottle(max_connections=200,
+            min_connections=50,
+            max_connection_rate=50,
+            rate_interval_seconds=10)
+
+        balancer = self.driver.ex_balancer_update_connection_throttle(balancer,
+            connection_throttle)
+        updated_throttle = balancer.extra['connectionThrottle']
+
+        self.assertEquals(200, updated_throttle.max_connections)
+        self.assertEquals(50, updated_throttle.min_connections)
+        self.assertEquals(50, updated_throttle.max_connection_rate)
+        self.assertEquals(10, updated_throttle.rate_interval_seconds)
+
+    def test_balancer_update_connection_throttle_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        connection_throttle = RackspaceConnectionThrottle(max_connections=200,
+            min_connections=50,
+            max_connection_rate=50,
+            rate_interval_seconds=10)
+
+        resp = self.driver.ex_balancer_update_connection_throttle_no_poll(
+            balancer, connection_throttle)
+
+        self.assertTrue(resp)
+
+    def test_balancer_disable_connection_throttle(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_balancer_disable_connection_throttle(
+            balancer)
+
+        self.assertTrue('connectionThrottle' not in balancer.extra)
+
+    def test_balancer_disable_connection_throttle_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        resp = self.driver.ex_balancer_disable_connection_throttle_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -543,9 +585,15 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_8290_connectionthrottle(self, method, url, body, headers):
+        if method == 'DELETE':
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers):
         if method == "DELETE":
-            return (httplib.ACCEPTED, "", {}, httplib.responses[httplib.ACCEPTED])
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
@@ -625,6 +673,19 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94695_connectionthrottle(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertEquals(50, json_body['minConnections'])
+            self.assertEquals(200, json_body['maxConnections'])
+            self.assertEquals(50, json_body['maxConnectionRate'])
+            self.assertEquals(10, json_body['rateInterval'])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json")
-- 
1.7.5.4


From 706121a897a898d3072e329b38ffdbfbf11d68ac Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 11:10:03 -0500
Subject: [PATCH 12/32] Enable/disable connection logging.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index dd8cb91..2b2b700 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -487,6 +487,65 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_enable_connection_logging(self, balancer):
+        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+            "connectionLogging": {
+                "enabled": True
+            }
+        }))
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Enable connection logging request not accepted')
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_balancer_enable_connection_logging_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
+
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+            "connectionLogging": {
+                "enabled": True
+            }
+        }))
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_balancer_disable_connection_logging(self, balancer):
+        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+            "connectionLogging": {
+                "enabled": False
+            }
+        }))
+
+        if resp.status != httplib.ACCEPTED:
+            raise LibcloudError('Disable connection logging request not accepted')
+
+        resp = self.connection.async_request(
+            action='/loadbalancers/%s' % balancer.id,
+            method='GET')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_balancer_disable_connection_logging_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+            "connectionLogging": {
+                "enabled": False
+            }
+        }))
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index f233c59..c8c567a 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -367,6 +367,36 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_balancer_enable_connection_logging(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        balancer = self.driver.ex_balancer_enable_connection_logging(
+            balancer)
+
+        self.assertTrue(balancer.extra["connectionLoggingEnabled"])
+
+    def test_balancer_enable_connection_logging_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        resp = self.driver.ex_balancer_enable_connection_logging_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
+    def test_balancer_disable_connection_logging(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_balancer_disable_connection_logging(
+            balancer
+        )
+
+        self.assertFalse(balancer.extra["connectionLoggingEnabled"])
+
+    def test_balancer_disable_connection_logging_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        resp = self.driver.ex_balancer_disable_connection_logging_no_poll(
+            balancer
+        )
+
+        self.assertTrue(resp)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -591,6 +621,16 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_8290_connectionlogging(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertFalse(json_body["connectionLogging"]["enabled"])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers):
         if method == "DELETE":
             return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
@@ -686,6 +726,16 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94695_connectionlogging(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertTrue(json_body["connectionLogging"]["enabled"])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json")
-- 
1.7.5.4


From 012f8c22f46347330bfbe22b558daf9bd91ba7f8 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 11:16:07 -0500
Subject: [PATCH 13/32] Clean up boilerplate.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 2b2b700..eb42d95 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -396,22 +396,21 @@ class RackspaceLBDriver(Driver):
 
         return [self._to_access_rule(el) for el in resp.object["accessList"]]
 
-    def ex_balancer_update_health_monitor(self, balancer, health_monitor):
-        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
-
-        resp = self.connection.request(uri,
-            method='PUT',
-            data=json.dumps(health_monitor.to_dict()))
-
-        if resp.status != httplib.ACCEPTED:
-            raise LibcloudError('Update health monitor request not accepted')
-
+    def _get_updated_balancer(self, balancer):
         resp = self.connection.async_request(
             action='/loadbalancers/%s' % balancer.id,
             method='GET')
 
         return self._to_balancer(resp.object['loadBalancer'])
 
+    def ex_balancer_update_health_monitor(self, balancer, health_monitor):
+        accepted = self.ex_balancer_update_health_monitor_no_poll(balancer,
+                    health_monitor)
+        if not accepted:
+            raise LibcloudError('Update health monitor request not accepted')
+
+        return self._get_updated_balancer(balancer)
+
     def ex_balancer_update_health_monitor_no_poll(self, balancer,
                                                   health_monitor):
         uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
@@ -423,19 +422,10 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_balancer_disable_health_monitor(self, balancer):
-        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
-
-        resp = self.connection.request(uri,
-            method='DELETE')
-
-        if resp.status != httplib.ACCEPTED:
+        if not self.ex_balancer_disable_health_monitor_no_poll(balancer):
             raise LibcloudError('Disable health monitor request not accepted')
 
-        resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        return self._to_balancer(resp.object['loadBalancer'])
+        return self._get_updated_balancer(balancer)
 
     def ex_balancer_disable_health_monitor_no_poll(self, balancer):
         uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
@@ -447,18 +437,13 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_update_connection_throttle(self, balancer,
                                                connection_throttle):
-        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
-        resp = self.connection.request(uri, method='PUT',
-            data=json.dumps(connection_throttle.to_dict()))
+        accepted = self.ex_balancer_update_connection_throttle_no_poll(
+            balancer, connection_throttle)
 
-        if resp.status != httplib.ACCEPTED:
+        if not accepted:
             raise LibcloudError('Update connection throttle request not accepted')
 
-        resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        return self._to_balancer(resp.object['loadBalancer'])
+        return self._get_updated_balancer(balancer)
 
     def ex_balancer_update_connection_throttle_no_poll(self, balancer,
                                                        connection_throttle):
@@ -469,17 +454,10 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_balancer_disable_connection_throttle(self, balancer):
-        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
-        resp = self.connection.request(uri, method='DELETE')
-
-        if resp.status != httplib.ACCEPTED:
+        if not self.ex_balancer_disable_connection_throttle_no_poll(balancer):
             raise LibcloudError('Disable connection throttle request not accepted')
 
-        resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        return self._to_balancer(resp.object['loadBalancer'])
+        return self._get_updated_balancer(balancer)
 
     def ex_balancer_disable_connection_throttle_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
@@ -488,22 +466,10 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_balancer_enable_connection_logging(self, balancer):
-        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
-        resp = self.connection.request(uri, method='PUT',
-            data=json.dumps({
-            "connectionLogging": {
-                "enabled": True
-            }
-        }))
-
-        if resp.status != httplib.ACCEPTED:
+        if not self.ex_balancer_enable_connection_logging_no_poll(balancer):
             raise LibcloudError('Enable connection logging request not accepted')
 
-        resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        return self._to_balancer(resp.object['loadBalancer'])
+        return self._get_updated_balancer(balancer)
 
     def ex_balancer_enable_connection_logging_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
@@ -518,22 +484,10 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_balancer_disable_connection_logging(self, balancer):
-        uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
-        resp = self.connection.request(uri, method='PUT',
-            data=json.dumps({
-            "connectionLogging": {
-                "enabled": False
-            }
-        }))
-
-        if resp.status != httplib.ACCEPTED:
+        if not self.ex_balancer_disable_connection_logging_no_poll(balancer):
             raise LibcloudError('Disable connection logging request not accepted')
 
-        resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        return self._to_balancer(resp.object['loadBalancer'])
+        return self._get_updated_balancer(balancer)
 
     def ex_balancer_disable_connection_logging_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
-- 
1.7.5.4


From 1a80a3ab8488db6a1fa696d4503d3a80f1c1657a Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 12:00:24 -0500
Subject: [PATCH 14/32] Enable/disable session persistence with/without
 polling.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index eb42d95..7d6dc94 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -407,7 +407,8 @@ class RackspaceLBDriver(Driver):
         accepted = self.ex_balancer_update_health_monitor_no_poll(balancer,
                     health_monitor)
         if not accepted:
-            raise LibcloudError('Update health monitor request not accepted')
+            msg = 'Update health monitor request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -423,7 +424,8 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_disable_health_monitor(self, balancer):
         if not self.ex_balancer_disable_health_monitor_no_poll(balancer):
-            raise LibcloudError('Disable health monitor request not accepted')
+            msg = 'Disable health monitor request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -441,7 +443,8 @@ class RackspaceLBDriver(Driver):
             balancer, connection_throttle)
 
         if not accepted:
-            raise LibcloudError('Update connection throttle request not accepted')
+            msg = 'Update connection throttle request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -455,7 +458,8 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_disable_connection_throttle(self, balancer):
         if not self.ex_balancer_disable_connection_throttle_no_poll(balancer):
-            raise LibcloudError('Disable connection throttle request not accepted')
+            msg = 'Disable connection throttle request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -467,7 +471,8 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_enable_connection_logging(self, balancer):
         if not self.ex_balancer_enable_connection_logging_no_poll(balancer):
-            raise LibcloudError('Enable connection logging request not accepted')
+            msg = 'Enable connection logging request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -476,8 +481,8 @@ class RackspaceLBDriver(Driver):
 
         resp = self.connection.request(uri, method='PUT',
             data=json.dumps({
-            "connectionLogging": {
-                "enabled": True
+            'connectionLogging': {
+                'enabled': True
             }
         }))
 
@@ -485,7 +490,8 @@ class RackspaceLBDriver(Driver):
 
     def ex_balancer_disable_connection_logging(self, balancer):
         if not self.ex_balancer_disable_connection_logging_no_poll(balancer):
-            raise LibcloudError('Disable connection logging request not accepted')
+            msg = 'Disable connection logging request not accepted'
+            raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
@@ -493,13 +499,46 @@ class RackspaceLBDriver(Driver):
         uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
         resp = self.connection.request(uri, method='PUT',
             data=json.dumps({
-            "connectionLogging": {
-                "enabled": False
+            'connectionLogging': {
+                'enabled': False
             }
         }))
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_enable_session_persistence(self, balancer):
+        if not self.ex_balancer_enable_session_persistence_no_poll(balancer):
+            msg = 'Enable session persistence request not accepted'
+            raise LibcloudError(msg)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_balancer_enable_session_persistence_no_poll(self, balancer):
+        # API currently only supports HTTP_COOKIE persistence type, and
+        # having this persistence type means it is enabled
+        uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+                'sessionPersistence': {
+                    'persistenceType': 'HTTP_COOKIE'
+                }
+            }))
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_balancer_disable_session_persistence(self, balancer):
+        if not self.ex_balancer_disable_session_persistence_no_poll(balancer):
+            msg = 'Disable session persistence request not accepted'
+            raise LibcloudError(msg)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_balancer_disable_session_persistence_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index c8c567a..ad2d3b3 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -397,6 +397,34 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_balancer_enable_session_persistence(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        balancer = self.driver.ex_balancer_enable_session_persistence(balancer)
+
+        persistence_type = balancer.extra['sessionPersistenceType']
+        self.assertEquals('HTTP_COOKIE', persistence_type)
+
+    def test_balancer_enable_session_persistence_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        resp = self.driver.ex_balancer_enable_session_persistence_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
+    def test_balancer_disable_session_persistence(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_balancer_disable_session_persistence(
+            balancer)
+
+        self.assertTrue('sessionPersistenceType' not in balancer.extra)
+
+    def test_balancer_disable_session_persistence_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        resp = self.driver.ex_balancer_disable_session_persistence_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -615,6 +643,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers):
+        if method == "DELETE":
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_8290_connectionthrottle(self, method, url, body, headers):
         if method == 'DELETE':
             return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
@@ -622,6 +656,7 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_8290_connectionlogging(self, method, url, body, headers):
+        # Connection Logging uses a PUT to disable connection logging
         if method == 'PUT':
             json_body = json.loads(body)
 
@@ -631,7 +666,7 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
-    def _v1_0_slug_loadbalancers_8290_healthmonitor(self, method, url, body, headers):
+    def _v1_0_slug_loadbalancers_8290_sessionpersistence(self, method, url, body, headers):
         if method == "DELETE":
             return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
@@ -736,6 +771,17 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94695_sessionpersistence(self, method, url, body, headers):
+        if method == 'PUT':
+            json_body = json.loads(body)
+
+            persistence_type = json_body['sessionPersistence']['persistenceType']
+            self.assertEquals('HTTP_COOKIE', persistence_type)
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json")
-- 
1.7.5.4


From 78373c500cb4d88f96254d05ec8d3d035397a868 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 12:53:34 -0500
Subject: [PATCH 15/32] Add update/disable custom error page with/without
 polling.


 create mode 100644 test/loadbalancer/fixtures/rackspace/error_page_default.json
 create mode 100644 test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json

diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 7d6dc94..b398f5e 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -539,6 +539,38 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_update_error_page(self, balancer, page_content):
+        accepted = self.ex_balancer_update_error_page_no_poll(balancer, page_content)
+        if not accepted:
+            msg = 'Update error page request not accepted'
+            raise LibcloudError(msg)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_balancer_update_error_page_no_poll(self, balancer, page_content):
+        uri = '/loadbalancers/%s/errorpage' % (balancer.id)
+        resp = self.connection.request(uri, method='PUT',
+            data=json.dumps({
+                'errorpage': {
+                    'content': page_content
+                }
+            }))
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_balancer_disable_custom_error_page(self, balancer):
+        if not self.ex_balancer_disable_custom_error_page_no_poll(balancer):
+            msg = 'Disable custom error page request not accepted'
+            raise LibcloudError(msg)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_balancer_disable_custom_error_page_no_poll(self, balancer):
+        uri = '/loadbalancers/%s/errorpage' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/fixtures/rackspace/error_page_default.json b/test/loadbalancer/fixtures/rackspace/error_page_default.json
new file mode 100644
index 0000000..76d8189
--- /dev/null
+++ b/test/loadbalancer/fixtures/rackspace/error_page_default.json
@@ -0,0 +1 @@
+{"errorpage":{"content":"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"><title>Service Unavailable</title><style type=\"text/css\">body, p, h1 {font-family: Verdana, Arial, Helvetica, sans-serif;}h2 {font-family: Arial, Helvetica, sans-serif;color: #b10b29;}</style></head><body><h2>Service Unavailable</h2><p>The service is temporarily unavailable. Please try again later.</p></body></html>"}}
\ No newline at end of file
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json
new file mode 100644
index 0000000..88f44c8
--- /dev/null
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json
@@ -0,0 +1 @@
+{"errorpage":{"content":"<html>Generic Error Page</html>"}}
\ No newline at end of file
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index ad2d3b3..d94c2ca 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -425,6 +425,42 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_ex_balancer_update_error_page(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        content = "<html>Generic Error Page</html>"
+        balancer = self.driver.ex_balancer_update_error_page(
+            balancer, content)
+
+        error_page_content = self.driver.ex_get_balancer_error_page(balancer)
+        self.assertEquals(content, error_page_content)
+
+    def test_ex_balancer_update_error_page_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        content = "<html>Generic Error Page</html>"
+        resp = self.driver.ex_balancer_update_error_page_no_poll(
+            balancer, content)
+
+        self.assertTrue(resp)
+
+    def test_ex_balancer_disable_custom_error_page_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        resp = self.driver.ex_balancer_disable_custom_error_page_no_poll(balancer)
+
+        self.assertTrue(resp)
+
+    def test_ex_balancer_disable_custom_error_page(self):
+        fixtures = LoadBalancerFileFixtures('rackspace')
+        error_page_fixture = json.loads(
+            fixtures.load('error_page_default.json'))
+
+        default_error_page = error_page_fixture['errorpage']['content']
+
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        balancer = self.driver.ex_balancer_disable_custom_error_page(balancer)
+
+        error_page_content = self.driver.ex_get_balancer_error_page(balancer)
+        self.assertEquals(default_error_page, error_page_content)
+
     def test_balancer_list_members(self):
         expected = set(['10.1.0.10:80', '10.1.0.11:80', '10.1.0.9:8080'])
         balancer = self.driver.get_balancer(balancer_id='8290')
@@ -667,7 +703,20 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_8290_sessionpersistence(self, method, url, body, headers):
-        if method == "DELETE":
+        if method == 'DELETE':
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
+    def _v1_0_slug_loadbalancers_8290_errorpage(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load('v1_slug_loadbalancers_8290_errorpage.json')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+        elif method == 'PUT':
+            json_body = json.loads(body)
+
+            self.assertEquals('<html>Generic Error Page</html>',
+                json_body['errorpage']['content'])
             return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
@@ -782,6 +831,15 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94695_errorpage(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load("error_page_default.json")
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+        elif method == 'DELETE':
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94696(self, method, url, body, headers):
         if method == "GET":
             body = self.fixtures.load("v1_slug_loadbalancers_94696_http_health_monitor.json")
-- 
1.7.5.4


From 272cced7a8a2d0dd037729f0193208dd49b483c2 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 13:06:03 -0500
Subject: [PATCH 16/32] Make names better (ex_disable_balancer_xxx instead of
 ex_balancer_disable_xxx).


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b398f5e..753d424 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -403,8 +403,8 @@ class RackspaceLBDriver(Driver):
 
         return self._to_balancer(resp.object['loadBalancer'])
 
-    def ex_balancer_update_health_monitor(self, balancer, health_monitor):
-        accepted = self.ex_balancer_update_health_monitor_no_poll(balancer,
+    def ex_update_balancer_health_monitor(self, balancer, health_monitor):
+        accepted = self.ex_update_balancer_health_monitor_no_poll(balancer,
                     health_monitor)
         if not accepted:
             msg = 'Update health monitor request not accepted'
@@ -412,7 +412,7 @@ class RackspaceLBDriver(Driver):
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_update_health_monitor_no_poll(self, balancer,
+    def ex_update_balancer_health_monitor_no_poll(self, balancer,
                                                   health_monitor):
         uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
 
@@ -422,14 +422,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_disable_health_monitor(self, balancer):
-        if not self.ex_balancer_disable_health_monitor_no_poll(balancer):
+    def ex_disable_balancer_health_monitor(self, balancer):
+        if not self.ex_disable_balancer_health_monitor_no_poll(balancer):
             msg = 'Disable health monitor request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_disable_health_monitor_no_poll(self, balancer):
+    def ex_disable_balancer_health_monitor_no_poll(self, balancer):
         uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
 
         resp = self.connection.request(uri,
@@ -437,9 +437,9 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_update_connection_throttle(self, balancer,
+    def ex_update_balancer_connection_throttle(self, balancer,
                                                connection_throttle):
-        accepted = self.ex_balancer_update_connection_throttle_no_poll(
+        accepted = self.ex_update_balancer_connection_throttle_no_poll(
             balancer, connection_throttle)
 
         if not accepted:
@@ -448,7 +448,7 @@ class RackspaceLBDriver(Driver):
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_update_connection_throttle_no_poll(self, balancer,
+    def ex_update_balancer_connection_throttle_no_poll(self, balancer,
                                                        connection_throttle):
         uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
         resp = self.connection.request(uri, method='PUT',
@@ -456,14 +456,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_disable_connection_throttle(self, balancer):
-        if not self.ex_balancer_disable_connection_throttle_no_poll(balancer):
+    def ex_disable_balancer_connection_throttle(self, balancer):
+        if not self.ex_disable_balancer_connection_throttle_no_poll(balancer):
             msg = 'Disable connection throttle request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_disable_connection_throttle_no_poll(self, balancer):
+    def ex_disable_balancer_connection_throttle_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
         resp = self.connection.request(uri, method='DELETE')
 
@@ -488,14 +488,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_disable_connection_logging(self, balancer):
-        if not self.ex_balancer_disable_connection_logging_no_poll(balancer):
+    def ex_disable_balancer_connection_logging(self, balancer):
+        if not self.ex_disable_balancer_connection_logging_no_poll(balancer):
             msg = 'Disable connection logging request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_disable_connection_logging_no_poll(self, balancer):
+    def ex_disable_balancer_connection_logging_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
         resp = self.connection.request(uri, method='PUT',
             data=json.dumps({
@@ -526,28 +526,28 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_disable_session_persistence(self, balancer):
-        if not self.ex_balancer_disable_session_persistence_no_poll(balancer):
+    def ex_disable_balancer_session_persistence(self, balancer):
+        if not self.ex_disable_balancer_session_persistence_no_poll(balancer):
             msg = 'Disable session persistence request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_disable_session_persistence_no_poll(self, balancer):
+    def ex_disable_balancer_session_persistence_no_poll(self, balancer):
         uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
         resp = self.connection.request(uri, method='DELETE')
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_update_error_page(self, balancer, page_content):
-        accepted = self.ex_balancer_update_error_page_no_poll(balancer, page_content)
+    def ex_update_balancer_error_page(self, balancer, page_content):
+        accepted = self.ex_update_balancer_error_page_no_poll(balancer, page_content)
         if not accepted:
             msg = 'Update error page request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_update_error_page_no_poll(self, balancer, page_content):
+    def ex_update_balancer_error_page_no_poll(self, balancer, page_content):
         uri = '/loadbalancers/%s/errorpage' % (balancer.id)
         resp = self.connection.request(uri, method='PUT',
             data=json.dumps({
@@ -558,14 +558,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_disable_custom_error_page(self, balancer):
-        if not self.ex_balancer_disable_custom_error_page_no_poll(balancer):
+    def ex_disable_balancer_custom_error_page(self, balancer):
+        if not self.ex_disable_balancer_custom_error_page_no_poll(balancer):
             msg = 'Disable custom error page request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_disable_custom_error_page_no_poll(self, balancer):
+    def ex_disable_balancer_custom_error_page_no_poll(self, balancer):
         uri = '/loadbalancers/%s/errorpage' % (balancer.id)
         resp = self.connection.request(uri, method='DELETE')
 
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index d94c2ca..3dc49f1 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -258,12 +258,12 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(allow_rule.address, "2001:4801:7901::6/64")
         self.assertEquals(allow_rule.rule_type, RackspaceAccessRuleType.ALLOW)
 
-    def test_balancer_update_health_monitor(self):
+    def test_update_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
             attempts_before_deactivation=2)
 
-        balancer = self.driver.ex_balancer_update_health_monitor(balancer, monitor)
+        balancer = self.driver.ex_update_balancer_health_monitor(balancer, monitor)
         updated_monitor = balancer.extra['healthMonitor']
 
         self.assertEquals('CONNECT', updated_monitor.type)
@@ -271,7 +271,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(5, updated_monitor.timeout)
         self.assertEquals(2, updated_monitor.attempts_before_deactivation)
 
-    def test_balancer_update_http_health_monitor(self):
+    def test_update_balancer_http_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94696')
         monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
             attempts_before_deactivation=2,
@@ -279,7 +279,7 @@ class RackspaceLBTests(unittest.TestCase):
             status_regex='^[234][0-9][0-9]$',
             body_regex='Hello World!')
 
-        balancer = self.driver.ex_balancer_update_health_monitor(balancer, monitor)
+        balancer = self.driver.ex_update_balancer_health_monitor(balancer, monitor)
         updated_monitor = balancer.extra['healthMonitor']
 
         self.assertEquals('HTTP', updated_monitor.type)
@@ -290,17 +290,17 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals('^[234][0-9][0-9]$', updated_monitor.status_regex)
         self.assertEquals('Hello World!', updated_monitor.body_regex)
 
-    def test_balancer_update_health_monitor_no_poll(self):
+    def test_update_balancer_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
             attempts_before_deactivation=2)
 
-        resp = self.driver.ex_balancer_update_health_monitor_no_poll(balancer,
+        resp = self.driver.ex_update_balancer_health_monitor_no_poll(balancer,
             monitor)
 
         self.assertTrue(resp)
 
-    def test_balancer_update_http_health_monitor_no_poll(self):
+    def test_update_balancer_http_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94696')
         monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
             attempts_before_deactivation=2,
@@ -308,31 +308,31 @@ class RackspaceLBTests(unittest.TestCase):
             status_regex='^[234][0-9][0-9]$',
             body_regex='Hello World!')
 
-        resp = self.driver.ex_balancer_update_health_monitor_no_poll(balancer,
+        resp = self.driver.ex_update_balancer_health_monitor_no_poll(balancer,
             monitor)
 
         self.assertTrue(resp)
 
-    def test_balancer_disable_health_monitor(self):
+    def test_disable_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        balancer = self.driver.ex_balancer_disable_health_monitor(balancer)
+        balancer = self.driver.ex_disable_balancer_health_monitor(balancer)
 
         self.assertTrue('healthMonitor' not in balancer.extra)
 
-    def test_balancer_disable_health_monitor_no_poll(self):
+    def test_disable_balancer_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        resp = self.driver.ex_balancer_disable_health_monitor_no_poll(balancer)
+        resp = self.driver.ex_disable_balancer_health_monitor_no_poll(balancer)
 
         self.assertTrue(resp)
 
-    def test_balancer_update_connection_throttle(self):
+    def test_update_balancer_connection_throttle(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         connection_throttle = RackspaceConnectionThrottle(max_connections=200,
             min_connections=50,
             max_connection_rate=50,
             rate_interval_seconds=10)
 
-        balancer = self.driver.ex_balancer_update_connection_throttle(balancer,
+        balancer = self.driver.ex_update_balancer_connection_throttle(balancer,
             connection_throttle)
         updated_throttle = balancer.extra['connectionThrottle']
 
@@ -341,28 +341,28 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(50, updated_throttle.max_connection_rate)
         self.assertEquals(10, updated_throttle.rate_interval_seconds)
 
-    def test_balancer_update_connection_throttle_no_poll(self):
+    def test_update_balancer_connection_throttle_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         connection_throttle = RackspaceConnectionThrottle(max_connections=200,
             min_connections=50,
             max_connection_rate=50,
             rate_interval_seconds=10)
 
-        resp = self.driver.ex_balancer_update_connection_throttle_no_poll(
+        resp = self.driver.ex_update_balancer_connection_throttle_no_poll(
             balancer, connection_throttle)
 
         self.assertTrue(resp)
 
-    def test_balancer_disable_connection_throttle(self):
+    def test_disable_balancer_connection_throttle(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        balancer = self.driver.ex_balancer_disable_connection_throttle(
+        balancer = self.driver.ex_disable_balancer_connection_throttle(
             balancer)
 
         self.assertTrue('connectionThrottle' not in balancer.extra)
 
-    def test_balancer_disable_connection_throttle_no_poll(self):
+    def test_disable_balancer_connection_throttle_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        resp = self.driver.ex_balancer_disable_connection_throttle_no_poll(
+        resp = self.driver.ex_disable_balancer_connection_throttle_no_poll(
             balancer)
 
         self.assertTrue(resp)
@@ -381,17 +381,17 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_balancer_disable_connection_logging(self):
+    def test_disable_balancer_connection_logging(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        balancer = self.driver.ex_balancer_disable_connection_logging(
+        balancer = self.driver.ex_disable_balancer_connection_logging(
             balancer
         )
 
         self.assertFalse(balancer.extra["connectionLoggingEnabled"])
 
-    def test_balancer_disable_connection_logging_no_poll(self):
+    def test_disable_balancer_connection_logging_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        resp = self.driver.ex_balancer_disable_connection_logging_no_poll(
+        resp = self.driver.ex_disable_balancer_connection_logging_no_poll(
             balancer
         )
 
@@ -411,44 +411,44 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_balancer_disable_session_persistence(self):
+    def test_disable_balancer_session_persistence(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        balancer = self.driver.ex_balancer_disable_session_persistence(
+        balancer = self.driver.ex_disable_balancer_session_persistence(
             balancer)
 
         self.assertTrue('sessionPersistenceType' not in balancer.extra)
 
-    def test_balancer_disable_session_persistence_no_poll(self):
+    def test_disable_balancer_session_persistence_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
-        resp = self.driver.ex_balancer_disable_session_persistence_no_poll(
+        resp = self.driver.ex_disable_balancer_session_persistence_no_poll(
             balancer)
 
         self.assertTrue(resp)
 
-    def test_ex_balancer_update_error_page(self):
+    def test_ex_update_balancer_error_page(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         content = "<html>Generic Error Page</html>"
-        balancer = self.driver.ex_balancer_update_error_page(
+        balancer = self.driver.ex_update_balancer_error_page(
             balancer, content)
 
         error_page_content = self.driver.ex_get_balancer_error_page(balancer)
         self.assertEquals(content, error_page_content)
 
-    def test_ex_balancer_update_error_page_no_poll(self):
+    def test_ex_update_balancer_error_page_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         content = "<html>Generic Error Page</html>"
-        resp = self.driver.ex_balancer_update_error_page_no_poll(
+        resp = self.driver.ex_update_balancer_error_page_no_poll(
             balancer, content)
 
         self.assertTrue(resp)
 
-    def test_ex_balancer_disable_custom_error_page_no_poll(self):
+    def test_ex_disable_balancer_custom_error_page_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
-        resp = self.driver.ex_balancer_disable_custom_error_page_no_poll(balancer)
+        resp = self.driver.ex_disable_balancer_custom_error_page_no_poll(balancer)
 
         self.assertTrue(resp)
 
-    def test_ex_balancer_disable_custom_error_page(self):
+    def test_ex_disable_balancer_custom_error_page(self):
         fixtures = LoadBalancerFileFixtures('rackspace')
         error_page_fixture = json.loads(
             fixtures.load('error_page_default.json'))
@@ -456,7 +456,7 @@ class RackspaceLBTests(unittest.TestCase):
         default_error_page = error_page_fixture['errorpage']['content']
 
         balancer = self.driver.get_balancer(balancer_id='94695')
-        balancer = self.driver.ex_balancer_disable_custom_error_page(balancer)
+        balancer = self.driver.ex_disable_balancer_custom_error_page(balancer)
 
         error_page_content = self.driver.ex_get_balancer_error_page(balancer)
         self.assertEquals(default_error_page, error_page_content)
-- 
1.7.5.4


From eb2f8bbcfc8481751a2a26cd86b9b01f4ebc6da4 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 13:12:11 -0500
Subject: [PATCH 17/32] Single quotes instead of double quotes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 753d424..72b72ff 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -123,9 +123,9 @@ class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor):
 
     def to_dict(self):
         super_dict = super(RackspaceHTTPHealthMonitor, self).to_dict()
-        super_dict["path"] = self.path
-        super_dict["statusRegex"] = self.status_regex
-        super_dict["bodyRegex"] = self.body_regex
+        super_dict['path'] = self.path
+        super_dict['statusRegex'] = self.status_regex
+        super_dict['bodyRegex'] = self.body_regex
 
         return super_dict
 
-- 
1.7.5.4


From d812dd0fd4d8a58120a463937c0beeeccf2bfc2d Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 13:12:22 -0500
Subject: [PATCH 18/32] enable_balancer_xxx, not balancer_enable_xxx.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 72b72ff..405b0c2 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -469,14 +469,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_enable_connection_logging(self, balancer):
-        if not self.ex_balancer_enable_connection_logging_no_poll(balancer):
+    def ex_enable_balancer_connection_logging(self, balancer):
+        if not self.ex_enable_balancer_connection_logging_no_poll(balancer):
             msg = 'Enable connection logging request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_enable_connection_logging_no_poll(self, balancer):
+    def ex_enable_balancer_connection_logging_no_poll(self, balancer):
         uri = '/loadbalancers/%s/connectionlogging' % (balancer.id)
 
         resp = self.connection.request(uri, method='PUT',
@@ -506,14 +506,14 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_enable_session_persistence(self, balancer):
-        if not self.ex_balancer_enable_session_persistence_no_poll(balancer):
+    def ex_enable_balancer_session_persistence(self, balancer):
+        if not self.ex_enable_balancer_session_persistence_no_poll(balancer):
             msg = 'Enable session persistence request not accepted'
             raise LibcloudError(msg)
 
         return self._get_updated_balancer(balancer)
 
-    def ex_balancer_enable_session_persistence_no_poll(self, balancer):
+    def ex_enable_balancer_session_persistence_no_poll(self, balancer):
         # API currently only supports HTTP_COOKIE persistence type, and
         # having this persistence type means it is enabled
         uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 3dc49f1..dac77c2 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -367,16 +367,16 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_balancer_enable_connection_logging(self):
+    def test_enable_balancer_connection_logging(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
-        balancer = self.driver.ex_balancer_enable_connection_logging(
+        balancer = self.driver.ex_enable_balancer_connection_logging(
             balancer)
 
         self.assertTrue(balancer.extra["connectionLoggingEnabled"])
 
-    def test_balancer_enable_connection_logging_no_poll(self):
+    def test_enable_balancer_connection_logging_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
-        resp = self.driver.ex_balancer_enable_connection_logging_no_poll(
+        resp = self.driver.ex_enable_balancer_connection_logging_no_poll(
             balancer)
 
         self.assertTrue(resp)
@@ -397,16 +397,16 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_balancer_enable_session_persistence(self):
+    def test_ex_enable_balancer_session_persistence(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
-        balancer = self.driver.ex_balancer_enable_session_persistence(balancer)
+        balancer = self.driver.ex_enable_balancer_session_persistence(balancer)
 
         persistence_type = balancer.extra['sessionPersistenceType']
         self.assertEquals('HTTP_COOKIE', persistence_type)
 
-    def test_balancer_enable_session_persistence_no_poll(self):
+    def test_ex_enable_balancer_session_persistence_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
-        resp = self.driver.ex_balancer_enable_session_persistence_no_poll(
+        resp = self.driver.ex_enable_balancer_session_persistence_no_poll(
             balancer)
 
         self.assertTrue(resp)
-- 
1.7.5.4


From eeac68c3792c6d3ecbf29ca38eb78bd80bf0d02f Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 13:15:30 -0500
Subject: [PATCH 19/32] Closing angle braces for repr.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 405b0c2..249a651 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -76,7 +76,7 @@ class RackspaceHealthMonitor(object):
 
     def __repr__(self):
         return ('<RackspaceHealthMonitor: type=%s, delay=%d, timeout=%d, '
-                'attempts_before_deactivation=%d' %
+                'attempts_before_deactivation=%d>' %
                 (self.type, self.delay, self.timeout,
                  self.attempts_before_deactivation))
 
@@ -116,7 +116,7 @@ class RackspaceHTTPHealthMonitor(RackspaceHealthMonitor):
     def __repr__(self):
         return ('<RackspaceHTTPHealthMonitor: type=%s, delay=%d, timeout=%d, '
                 'attempts_before_deactivation=%d, path=%s, body_regex=%s, '
-                'status_regex=%s' %
+                'status_regex=%s>' %
                 (self.type, self.delay, self.timeout,
                  self.attempts_before_deactivation, self.path, self.body_regex,
                  self.status_regex))
@@ -164,7 +164,7 @@ class RackspaceConnectionThrottle(object):
     def __repr__(self):
         return ('<RackspaceConnectionThrottle: min_connections=%d, '
                 'max_connections=%d, max_connection_rate=%d, '
-                'rate_interval_seconds=%d' %
+                'rate_interval_seconds=%d>' %
                 (self.min_connections, self.max_connections,
                  self.max_connection_rate, self.rate_interval_seconds))
 
-- 
1.7.5.4


From 94654f2feb3a334b06307e3e4a151cd1180b9d8f Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 13:36:04 -0500
Subject: [PATCH 20/32] Make test names consistent.


diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index dac77c2..636a2ff 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -258,7 +258,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(allow_rule.address, "2001:4801:7901::6/64")
         self.assertEquals(allow_rule.rule_type, RackspaceAccessRuleType.ALLOW)
 
-    def test_update_balancer_health_monitor(self):
+    def test_ex_update_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
             attempts_before_deactivation=2)
@@ -271,7 +271,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(5, updated_monitor.timeout)
         self.assertEquals(2, updated_monitor.attempts_before_deactivation)
 
-    def test_update_balancer_http_health_monitor(self):
+    def test_ex_update_balancer_http_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94696')
         monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
             attempts_before_deactivation=2,
@@ -290,7 +290,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals('^[234][0-9][0-9]$', updated_monitor.status_regex)
         self.assertEquals('Hello World!', updated_monitor.body_regex)
 
-    def test_update_balancer_health_monitor_no_poll(self):
+    def test_ex_update_balancer_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
             attempts_before_deactivation=2)
@@ -300,7 +300,7 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_update_balancer_http_health_monitor_no_poll(self):
+    def test_ex_update_balancer_http_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94696')
         monitor = RackspaceHTTPHealthMonitor(type='HTTP', delay=10, timeout=5,
             attempts_before_deactivation=2,
@@ -313,19 +313,19 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_disable_balancer_health_monitor(self):
+    def test_ex_disable_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         balancer = self.driver.ex_disable_balancer_health_monitor(balancer)
 
         self.assertTrue('healthMonitor' not in balancer.extra)
 
-    def test_disable_balancer_health_monitor_no_poll(self):
+    def test_ex_disable_balancer_health_monitor_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         resp = self.driver.ex_disable_balancer_health_monitor_no_poll(balancer)
 
         self.assertTrue(resp)
 
-    def test_update_balancer_connection_throttle(self):
+    def test_ex_update_balancer_connection_throttle(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         connection_throttle = RackspaceConnectionThrottle(max_connections=200,
             min_connections=50,
@@ -341,7 +341,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(50, updated_throttle.max_connection_rate)
         self.assertEquals(10, updated_throttle.rate_interval_seconds)
 
-    def test_update_balancer_connection_throttle_no_poll(self):
+    def test_ex_update_balancer_connection_throttle_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         connection_throttle = RackspaceConnectionThrottle(max_connections=200,
             min_connections=50,
@@ -353,35 +353,35 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_disable_balancer_connection_throttle(self):
+    def test_ex_disable_balancer_connection_throttle(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         balancer = self.driver.ex_disable_balancer_connection_throttle(
             balancer)
 
         self.assertTrue('connectionThrottle' not in balancer.extra)
 
-    def test_disable_balancer_connection_throttle_no_poll(self):
+    def test_ex_disable_balancer_connection_throttle_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         resp = self.driver.ex_disable_balancer_connection_throttle_no_poll(
             balancer)
 
         self.assertTrue(resp)
 
-    def test_enable_balancer_connection_logging(self):
+    def test_ex_enable_balancer_connection_logging(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         balancer = self.driver.ex_enable_balancer_connection_logging(
             balancer)
 
         self.assertTrue(balancer.extra["connectionLoggingEnabled"])
 
-    def test_enable_balancer_connection_logging_no_poll(self):
+    def test_ex_enable_balancer_connection_logging_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         resp = self.driver.ex_enable_balancer_connection_logging_no_poll(
             balancer)
 
         self.assertTrue(resp)
 
-    def test_disable_balancer_connection_logging(self):
+    def test_ex_disable_balancer_connection_logging(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         balancer = self.driver.ex_disable_balancer_connection_logging(
             balancer
@@ -389,7 +389,7 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertFalse(balancer.extra["connectionLoggingEnabled"])
 
-    def test_disable_balancer_connection_logging_no_poll(self):
+    def test_ex_disable_balancer_connection_logging_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         resp = self.driver.ex_disable_balancer_connection_logging_no_poll(
             balancer
-- 
1.7.5.4


From 3b8f70030a87f7f78ee5a81bcf436851e4645d0a Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 14:37:44 -0500
Subject: [PATCH 21/32] Add creation of access rules.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 249a651..c79521e 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -181,6 +181,11 @@ class RackspaceAccessRuleType(object):
     ALLOW = 0
     DENY = 1
 
+    _RULE_TYPE_STRING_MAP = {
+        ALLOW: "ALLOW",
+        DENY: "DENY"
+    }
+
 
 class RackspaceAccessRule(object):
     """
@@ -197,11 +202,25 @@ class RackspaceAccessRule(object):
     @type address: C{str}
     """
 
-    def __init__(self, id, rule_type, address):
+    def __init__(self, id=None, rule_type=None, address=None):
         self.id = id
         self.rule_type = rule_type
         self.address = address
 
+    def to_dict(self):
+        type_string = \
+            RackspaceAccessRuleType._RULE_TYPE_STRING_MAP[self.rule_type]
+
+        as_dict = {
+            'type': type_string,
+            'address' : self.address,
+        }
+
+        if self.id is not None:
+            as_dict['id'] = self.id
+
+        return as_dict
+
 
 class RackspaceConnection(OpenStackBaseConnection, PollingConnection):
     responseCls = RackspaceResponse
@@ -571,6 +590,37 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_create_balancer_access_rule(self, balancer, rule):
+        accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule)
+        if not accepted:
+            msg = 'Create access rule not accepted'
+            raise LibcloudError(msg)
+
+        balancer = self._get_updated_balancer(balancer)
+        access_list = self.ex_balancer_access_list(balancer)
+
+        # LB API does not return the ID for the newly created item,
+        # so we have to fudge it. Rule types and addresses are
+        # unique, so assuming the API works correctly, this will
+        # work.
+        created_rule = [r for r in access_list \
+                        if rule.rule_type == r.rule_type and \
+                           rule.address == r.address]
+
+        if not created_rule:
+            raise LibcloudError('Could not find created rule')
+
+        return created_rule[0]
+
+    def ex_create_balancer_access_rule_no_poll(self, balancer, rule):
+        uri = '/loadbalancers/%s/accesslist' % (balancer.id)
+        resp = self.connection.request(uri, method='POST',
+            data=json.dumps({
+                'networkItem': rule.to_dict()
+            }))
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 636a2ff..40466b3 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -26,7 +26,7 @@ from libcloud.utils.py3 import httplib
 
 from libcloud.loadbalancer.base import LoadBalancer, Member, Algorithm
 from libcloud.loadbalancer.types import MemberCondition
-from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver, RackspaceHealthMonitor, RackspaceHTTPHealthMonitor, RackspaceConnectionThrottle
+from libcloud.loadbalancer.drivers.rackspace import RackspaceLBDriver, RackspaceHealthMonitor, RackspaceHTTPHealthMonitor, RackspaceConnectionThrottle, RackspaceAccessRule
 from libcloud.loadbalancer.drivers.rackspace import RackspaceUKLBDriver
 from libcloud.loadbalancer.drivers.rackspace import RackspaceAccessRuleType
 from libcloud.common.types import LibcloudError
@@ -258,6 +258,27 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(allow_rule.address, "2001:4801:7901::6/64")
         self.assertEquals(allow_rule.rule_type, RackspaceAccessRuleType.ALLOW)
 
+    def test_ex_create_balancer_access_rule(self):
+        balancer = self.driver.get_balancer(balancer_id='18940')
+
+        rule = RackspaceAccessRule(rule_type=RackspaceAccessRuleType.DENY,
+            address='0.0.0.0/0')
+
+        rule = self.driver.ex_create_balancer_access_rule(balancer, rule)
+
+        self.assertEquals(2883, rule.id)
+
+    def test_ex_create_balancer_access_rule_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='18940')
+
+        rule = RackspaceAccessRule(rule_type=RackspaceAccessRuleType.DENY,
+            address='0.0.0.0/0')
+
+        resp = self.driver.ex_create_balancer_access_rule_no_poll(balancer,
+            rule)
+
+        self.assertTrue(resp)
+
     def test_ex_update_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
@@ -743,9 +764,16 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         raise NotImplementedError
 
     def _v1_0_slug_loadbalancers_18940_accesslist(self, method, url, body, headers):
-        if method == "GET":
-            body = self.fixtures.load("v1_slug_loadbalancers_18940_accesslist.json")
+        if method == 'GET':
+            body = self.fixtures.load('v1_slug_loadbalancers_18940_accesslist.json')
             return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+        elif method == 'POST':
+            json_body = json.loads(body)
+
+            self.assertEquals('0.0.0.0/0', json_body['networkItem']['address'])
+            self.assertEquals('DENY', json_body['networkItem']['type'])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
-- 
1.7.5.4


From 5b2a4f9ad168a175d4a45a19167a6aff02915110 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 14:56:24 -0500
Subject: [PATCH 22/32] Add accessList to extra.


 create mode 100644 test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json

diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index c79521e..b0d1058 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -685,6 +685,10 @@ class RackspaceLBDriver(Driver):
         if 'updated' in el:
             extra['updated'] = self._iso_to_datetime(el['updated']['time'])
 
+        if 'accessList' in el:
+            extra['accessList'] = [self._to_access_rule(rule) \
+                                   for rule in el['accessList']]
+
         return LoadBalancer(id=el["id"],
                 name=el["name"],
                 state=self.LB_STATE_MAP.get(
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json
new file mode 100644
index 0000000..01d8c5d
--- /dev/null
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json
@@ -0,0 +1 @@
+{"loadBalancer":{"name":"new ord balancer","id":94698,"protocol":"HTTPS","port":443,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n06.lbaas.ord1.rackspace.net"},"nodes":[{"address":"10.181.231.202","id":94692,"port":80,"status":"OFFLINE","condition":"DRAINING","weight":25},{"address":"10.181.238.11","id":97683,"port":443,"status":"OFFLINE","condition":"ENABLED","weight":1}],"created":{"time":"2011-12-09T13:30:40Z"},"healthMonitor":{"type":"CONNECT","delay":5,"timeout":10,"attemptsBeforeDeactivation":4},"virtualIps":[{"address":"50.56.49.149","id":2359,"type":"PUBLIC","ipVersion":"IPV4"}],"sourceAddresses":{"ipv6Public":"2001:4801:7901::6/64","ipv4Servicenet":"10.183.252.25","ipv4Public":"184.106.100.25"},"updated":{"time":"2012-01-05T19:31:38Z"},"accessList":[{"address":"0.0.0.0/0","id":2883,"type":"DENY"},{"address":"2001:4801:7901::6/64","id":2884,"type":"ALLOW"},{"address":"8.8.8.8/0","id":3006,"type":"DENY"}],"connectionThrottle":{"maxConnections":200,"minConnections":50,"maxConnectionRate":50,"rateInterval":10},"connectionLogging":{"enabled":true}}}
\ No newline at end of file
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 40466b3..26215cf 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -161,6 +161,28 @@ class RackspaceLBTests(unittest.TestCase):
         updated_8290 = datetime.datetime(2011, 4, 7, 16, 28, 12)
         self.assertEquals(updated_8290, balancer.extra['updated'])
 
+    def test_get_balancer_extra_access_list(self):
+        balancer = self.driver.get_balancer(balancer_id='94698')
+
+        access_list = balancer.extra['accessList']
+
+        self.assertEquals(3, len(access_list))
+        self.assertEquals(2883, access_list[0].id)
+        self.assertEquals("0.0.0.0/0", access_list[0].address)
+        self.assertEquals(RackspaceAccessRuleType.DENY,
+            access_list[0].rule_type)
+
+        self.assertEquals(2884, access_list[1].id)
+        self.assertEquals("2001:4801:7901::6/64",
+            access_list[1].address)
+        self.assertEquals(RackspaceAccessRuleType.ALLOW,
+            access_list[1].rule_type)
+
+        self.assertEquals(3006, access_list[2].id)
+        self.assertEquals("8.8.8.8/0", access_list[2].address)
+        self.assertEquals(RackspaceAccessRuleType.DENY,
+            access_list[2].rule_type)
+
     def test_get_balancer_algorithm(self):
         balancer = self.driver.get_balancer(balancer_id='8290')
         self.assertEquals(balancer.extra["algorithm"], Algorithm.RANDOM)
@@ -898,6 +920,13 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94698(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load("v1_slug_loadbalancers_94698_with_access_list.json")
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_3130(self, method, url, body, headers):
         """ update_balancer(b, protocol='HTTPS'), then get_balancer('3130') """
         if method == "PUT":
-- 
1.7.5.4


From 1c8c7405096eadd3405ad80e0b9a735238c9001d Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 15:07:48 -0500
Subject: [PATCH 23/32] Use balancer extra for finding newly created rule in
 access list.


 create mode 100644 test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json

diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b0d1058..307b473 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -597,7 +597,7 @@ class RackspaceLBDriver(Driver):
             raise LibcloudError(msg)
 
         balancer = self._get_updated_balancer(balancer)
-        access_list = self.ex_balancer_access_list(balancer)
+        access_list = balancer.extra['accessList']
 
         # LB API does not return the ID for the newly created item,
         # so we have to fudge it. Rule types and addresses are
diff --git a/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json
new file mode 100644
index 0000000..4f1621f
--- /dev/null
+++ b/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json
@@ -0,0 +1 @@
+{"accessList":[{"address":"0.0.0.0/0","id":2883,"type":"DENY"},{"address":"2001:4801:7901::6/64","id":2884,"type":"ALLOW"},{"address":"8.8.8.8/0","id":3006,"type":"DENY"}]}
\ No newline at end of file
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 26215cf..9493ddd 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -281,7 +281,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(allow_rule.rule_type, RackspaceAccessRuleType.ALLOW)
 
     def test_ex_create_balancer_access_rule(self):
-        balancer = self.driver.get_balancer(balancer_id='18940')
+        balancer = self.driver.get_balancer(balancer_id='94698')
 
         rule = RackspaceAccessRule(rule_type=RackspaceAccessRuleType.DENY,
             address='0.0.0.0/0')
@@ -291,7 +291,7 @@ class RackspaceLBTests(unittest.TestCase):
         self.assertEquals(2883, rule.id)
 
     def test_ex_create_balancer_access_rule_no_poll(self):
-        balancer = self.driver.get_balancer(balancer_id='18940')
+        balancer = self.driver.get_balancer(balancer_id='94698')
 
         rule = RackspaceAccessRule(rule_type=RackspaceAccessRuleType.DENY,
             address='0.0.0.0/0')
@@ -789,13 +789,6 @@ class RackspaceLBMockHttp(MockHttpTestCase):
         if method == 'GET':
             body = self.fixtures.load('v1_slug_loadbalancers_18940_accesslist.json')
             return (httplib.OK, body, {}, httplib.responses[httplib.OK])
-        elif method == 'POST':
-            json_body = json.loads(body)
-
-            self.assertEquals('0.0.0.0/0', json_body['networkItem']['address'])
-            self.assertEquals('DENY', json_body['networkItem']['type'])
-
-            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         raise NotImplementedError
 
@@ -927,6 +920,20 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94698_accesslist(self, method, url, body, headers):
+        if method == 'GET':
+            body = self.fixtures.load('v1_slug_loadbalancers_94698_accesslist.json')
+            return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+        elif method == 'POST':
+            json_body = json.loads(body)
+
+            self.assertEquals('0.0.0.0/0', json_body['networkItem']['address'])
+            self.assertEquals('DENY', json_body['networkItem']['type'])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_3130(self, method, url, body, headers):
         """ update_balancer(b, protocol='HTTPS'), then get_balancer('3130') """
         if method == "PUT":
-- 
1.7.5.4


From d72c2280a196f16a1c50b0491f9e99bba7fa4a66 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 15:18:06 -0500
Subject: [PATCH 24/32] Single quotes instead of double quotes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 307b473..ec95ac6 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -182,8 +182,8 @@ class RackspaceAccessRuleType(object):
     DENY = 1
 
     _RULE_TYPE_STRING_MAP = {
-        ALLOW: "ALLOW",
-        DENY: "DENY"
+        ALLOW: 'ALLOW',
+        DENY: 'DENY'
     }
 
 
-- 
1.7.5.4


From 453c94ff9f18cdfd25028e8f0f6610ecf2d346ed Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 15:48:12 -0500
Subject: [PATCH 25/32] Update docstring to be correct.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index ec95ac6..b38f2f1 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -193,10 +193,11 @@ class RackspaceAccessRule(object):
     incoming IPs.
 
     @param id: Unique identifier to refer to this rule by.
-    @type id: C{str}
+    @type id: C{int}
 
-    @param rule_type: ALLOW or DENY.
-    @type id: C{RackspaceAccessRuleType}
+    @param rule_type: RackspaceAccessRuleType.ALLOW or
+                      RackspaceAccessRuleType.DENY.
+    @type id: C{int}
 
     @param address: IP address or cidr (can be IPv4 or IPv6).
     @type address: C{str}
-- 
1.7.5.4


From c024ac2e34f1182d88e9e8489269acfad029ec01 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 15:48:27 -0500
Subject: [PATCH 26/32] Delete access rule.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index b38f2f1..5ae84e0 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -622,6 +622,20 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_delete_balancer_access_rule(self, balancer, rule):
+        accepted = self.ex_delete_balancer_access_rule_no_poll(balancer, rule)
+        if not accepted:
+            msg = 'Delete access rule not accepted'
+            raise LibcloudError(msg)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_delete_balancer_access_rule_no_poll(self, balancer, rule):
+        uri = '/loadbalancers/%s/accesslist/%s' % (balancer.id, rule.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 9493ddd..751fb37 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -301,6 +301,33 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_ex_delete_balancer_access_rule(self):
+        balancer = self.driver.get_balancer(balancer_id='94698')
+
+        rule = RackspaceAccessRule(id=1007,
+            rule_type=RackspaceAccessRuleType.ALLOW,
+            address="10.45.13.5/12"
+        )
+
+        balancer= self.driver.ex_delete_balancer_access_rule(balancer, rule)
+
+        rule_ids = [r.id for r in balancer.extra['accessList']]
+
+        self.assertTrue(1007 not in rule_ids)
+
+    def test_ex_delete_balancer_access_rule_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94698')
+
+        rule = RackspaceAccessRule(id=1007,
+            rule_type=RackspaceAccessRuleType.ALLOW,
+            address="10.45.13.5/12"
+        )
+
+        resp = self.driver.ex_delete_balancer_access_rule_no_poll(balancer,
+            rule)
+
+        self.assertTrue(resp)
+
     def test_ex_update_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
@@ -934,6 +961,12 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94698_accesslist_1007(self, method, url, body, headers):
+        if method == 'DELETE':
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_3130(self, method, url, body, headers):
         """ update_balancer(b, protocol='HTTPS'), then get_balancer('3130') """
         if method == "PUT":
-- 
1.7.5.4


From 04d99134904c024c2053f0d109a73bc148cb24fc Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 16:00:48 -0500
Subject: [PATCH 27/32] Rename non-polling deletes to no_poll.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 89d4e1e..34971bd 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -345,7 +345,7 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_destroy_balancers(self, balancers):
+    def ex_destroy_balancers_no_poll(self, balancers):
         ids = [('id', balancer.id) for balancer in balancers]
         resp = self.connection.request('/loadbalancers',
             method='DELETE',
@@ -383,7 +383,7 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_balancer_detach_members(self, balancer, members):
+    def ex_balancer_detach_members_no_poll(self, balancer, members):
         uri = '/loadbalancers/%s/nodes' % (balancer.id)
         ids = [('id', member.id) for member in members]
         resp = self.connection.request(uri, method='DELETE', params=ids)
@@ -645,7 +645,7 @@ class RackspaceLBDriver(Driver):
 
         return self._get_updated_balancer(balancer)
 
-    def ex_delete_balancer_access_rule_no_poll(self, balancer, rule):
+    def ex_destroy_balancer_access_rule_no_poll(self, balancer, rule):
         uri = '/loadbalancers/%s/accesslist/%s' % (balancer.id, rule.id)
         resp = self.connection.request(uri, method='DELETE')
 
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 534c4a0..05ebc99 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -111,7 +111,7 @@ class RackspaceLBTests(unittest.TestCase):
 
     def test_ex_destroy_balancers(self):
         balancers = self.driver.list_balancers()
-        ret = self.driver.ex_destroy_balancers(balancers)
+        ret = self.driver.ex_destroy_balancers_no_poll(balancers)
         self.assertTrue(ret)
 
     def test_get_balancer(self):
@@ -315,13 +315,13 @@ class RackspaceLBTests(unittest.TestCase):
             address="10.45.13.5/12"
         )
 
-        balancer= self.driver.ex_delete_balancer_access_rule(balancer, rule)
+        balancer= self.driver.ex_destroy_balancer_access_rule(balancer, rule)
 
         rule_ids = [r.id for r in balancer.extra['accessList']]
 
         self.assertTrue(1007 not in rule_ids)
 
-    def test_ex_delete_balancer_access_rule_no_poll(self):
+    def test_ex_destroy_balancer_access_rule_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94698')
 
         rule = RackspaceAccessRule(id=1007,
@@ -329,7 +329,7 @@ class RackspaceLBTests(unittest.TestCase):
             address="10.45.13.5/12"
         )
 
-        resp = self.driver.ex_delete_balancer_access_rule_no_poll(balancer,
+        resp = self.driver.ex_destroy_balancer_access_rule_no_poll(balancer,
             rule)
 
         self.assertTrue(resp)
@@ -591,7 +591,7 @@ class RackspaceLBTests(unittest.TestCase):
         balancer = self.driver.get_balancer(balancer_id='8290')
         members = balancer.list_members()
 
-        ret = self.driver.ex_balancer_detach_members(balancer, members)
+        ret = self.driver.ex_balancer_detach_members_no_poll(balancer, members)
         self.assertTrue(ret)
 
     def test_update_balancer_protocol(self):
-- 
1.7.5.4


From d14ed504cef91a7ed3ced9a5eddc95281cb6b5a5 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 16:25:17 -0500
Subject: [PATCH 28/32] Bulk destroy access rules.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 34971bd..40310b7 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -637,8 +637,8 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_delete_balancer_access_rule(self, balancer, rule):
-        accepted = self.ex_delete_balancer_access_rule_no_poll(balancer, rule)
+    def ex_destroy_balancer_access_rule(self, balancer, rule):
+        accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule)
         if not accepted:
             msg = 'Delete access rule not accepted'
             raise LibcloudError(msg)
@@ -651,6 +651,16 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_destroy_balancer_access_rules_no_poll(self, balancer, rules):
+        ids = [('id', rule.id) for rule in rules]
+        uri = '/loadbalancers/%s/accesslist/' % balancer.id
+
+        resp = self.connection.request(uri,
+            method='DELETE',
+            params=ids)
+
+        return resp.status == httplib.ACCEPTED
+
     def _to_protocols(self, object):
         protocols = []
         for item in object["protocols"]:
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 05ebc99..8bbea45 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -307,7 +307,7 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
-    def test_ex_delete_balancer_access_rule(self):
+    def test_ex_destroy_balancer_access_rule(self):
         balancer = self.driver.get_balancer(balancer_id='94698')
 
         rule = RackspaceAccessRule(id=1007,
@@ -334,6 +334,14 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_ex_destroy_balancer_access_rules_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94699')
+
+        resp = self.driver.ex_destroy_balancer_access_rules_no_poll(balancer,
+            balancer.extra['accessList'])
+
+        self.assertTrue(resp)
+
     def test_ex_update_balancer_health_monitor(self):
         balancer = self.driver.get_balancer(balancer_id='94695')
         monitor = RackspaceHealthMonitor(type='CONNECT', delay=10, timeout=5,
@@ -728,7 +736,7 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             body = self.fixtures.load('v1_slug_loadbalancers_post.json')
             return (httplib.ACCEPTED, body, {},
                     httplib.responses[httplib.ACCEPTED])
-        elif method == "DELETE":
+        elif method == 'DELETE':
             balancers = self.fixtures.load('v1_slug_loadbalancers.json')
             balancers_json = json.loads(balancers)
 
@@ -997,6 +1005,33 @@ class RackspaceLBMockHttp(MockHttpTestCase):
 
         raise NotImplementedError
 
+    def _v1_0_slug_loadbalancers_94699(self, method, url, body, headers):
+        if method == 'GET':
+            # Use the same fixture for batch deletes as for single deletes
+            body = self.fixtures.load('v1_slug_loadbalancers_94698_with_access_list.json')
+            json_body = json.loads(body)
+            json_body['loadBalancer']['id'] = 94699
+
+            updated_body = json.dumps(json_body)
+            return (httplib.OK, updated_body, {}, httplib.responses[httplib.OK])
+
+        raise NotImplementedError
+
+    def _v1_0_slug_loadbalancers_94699_accesslist(self, method, url, body, headers):
+        if method == 'DELETE':
+            fixture = 'v1_slug_loadbalancers_94698_with_access_list.json'
+            fixture_json = json.loads(self.fixtures.load(fixture))
+            access_list_json = fixture_json['loadBalancer']['accessList']
+
+            for access_rule in access_list_json:
+                id = access_rule['id']
+                self.assertTrue(urllib.urlencode([('id', id)]) in url,
+                    msg='Did not delete access rule with id %d' % id)
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
     def _v1_0_slug_loadbalancers_94698_accesslist_1007(self, method, url, body, headers):
         if method == 'DELETE':
             return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
-- 
1.7.5.4


From 7520a44aa7c88315505677a2d871ef75f9e14c4b Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 16:37:17 -0500
Subject: [PATCH 29/32] Polling access rule destroy/detach nodes.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 40310b7..5c58992 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -345,7 +345,7 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
-    def ex_destroy_balancers_no_poll(self, balancers):
+    def ex_destroy_balancers(self, balancers):
         ids = [('id', balancer.id) for balancer in balancers]
         resp = self.connection.request('/loadbalancers',
             method='DELETE',
@@ -383,6 +383,15 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_detach_members(self, balancer, members):
+        accepted = self.ex_balancer_detach_members_no_poll(balancer, members)
+
+        if not accepted:
+            msg = 'Detach members request was not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
     def ex_balancer_detach_members_no_poll(self, balancer, members):
         uri = '/loadbalancers/%s/nodes' % (balancer.id)
         ids = [('id', member.id) for member in members]
@@ -443,7 +452,7 @@ class RackspaceLBDriver(Driver):
                     health_monitor)
         if not accepted:
             msg = 'Update health monitor request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -460,7 +469,7 @@ class RackspaceLBDriver(Driver):
     def ex_disable_balancer_health_monitor(self, balancer):
         if not self.ex_disable_balancer_health_monitor_no_poll(balancer):
             msg = 'Disable health monitor request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -479,7 +488,7 @@ class RackspaceLBDriver(Driver):
 
         if not accepted:
             msg = 'Update connection throttle request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -494,7 +503,7 @@ class RackspaceLBDriver(Driver):
     def ex_disable_balancer_connection_throttle(self, balancer):
         if not self.ex_disable_balancer_connection_throttle_no_poll(balancer):
             msg = 'Disable connection throttle request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -507,7 +516,7 @@ class RackspaceLBDriver(Driver):
     def ex_enable_balancer_connection_logging(self, balancer):
         if not self.ex_enable_balancer_connection_logging_no_poll(balancer):
             msg = 'Enable connection logging request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -526,7 +535,7 @@ class RackspaceLBDriver(Driver):
     def ex_disable_balancer_connection_logging(self, balancer):
         if not self.ex_disable_balancer_connection_logging_no_poll(balancer):
             msg = 'Disable connection logging request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -544,7 +553,7 @@ class RackspaceLBDriver(Driver):
     def ex_enable_balancer_session_persistence(self, balancer):
         if not self.ex_enable_balancer_session_persistence_no_poll(balancer):
             msg = 'Enable session persistence request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -564,7 +573,7 @@ class RackspaceLBDriver(Driver):
     def ex_disable_balancer_session_persistence(self, balancer):
         if not self.ex_disable_balancer_session_persistence_no_poll(balancer):
             msg = 'Disable session persistence request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -578,7 +587,7 @@ class RackspaceLBDriver(Driver):
         accepted = self.ex_update_balancer_error_page_no_poll(balancer, page_content)
         if not accepted:
             msg = 'Update error page request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -596,7 +605,7 @@ class RackspaceLBDriver(Driver):
     def ex_disable_balancer_custom_error_page(self, balancer):
         if not self.ex_disable_balancer_custom_error_page_no_poll(balancer):
             msg = 'Disable custom error page request not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -610,7 +619,7 @@ class RackspaceLBDriver(Driver):
         accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule)
         if not accepted:
             msg = 'Create access rule not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         balancer = self._get_updated_balancer(balancer)
         access_list = balancer.extra['accessList']
@@ -641,7 +650,7 @@ class RackspaceLBDriver(Driver):
         accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule)
         if not accepted:
             msg = 'Delete access rule not accepted'
-            raise LibcloudError(msg)
+            raise LibcloudError(msg, driver=self)
 
         return self._get_updated_balancer(balancer)
 
@@ -651,6 +660,16 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_destroy_balancer_access_rules(self, balancer, rules):
+        accepted = self.ex_destroy_balancer_access_rules_no_poll(
+            balancer, rules)
+
+        if not accepted:
+            msg = 'Destroy access rules request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
     def ex_destroy_balancer_access_rules_no_poll(self, balancer, rules):
         ids = [('id', rule.id) for rule in rules]
         uri = '/loadbalancers/%s/accesslist/' % balancer.id
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 8bbea45..6641e09 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -111,7 +111,7 @@ class RackspaceLBTests(unittest.TestCase):
 
     def test_ex_destroy_balancers(self):
         balancers = self.driver.list_balancers()
-        ret = self.driver.ex_destroy_balancers_no_poll(balancers)
+        ret = self.driver.ex_destroy_balancers(balancers)
         self.assertTrue(ret)
 
     def test_get_balancer(self):
@@ -334,6 +334,13 @@ class RackspaceLBTests(unittest.TestCase):
 
         self.assertTrue(resp)
 
+    def test_ex_destroy_balancer_access_rules(self):
+        balancer = self.driver.get_balancer(balancer_id='94699')
+        balancer = self.driver.ex_destroy_balancer_access_rules(balancer,
+            balancer.extra['accessList'])
+
+        self.assertEquals('94699', balancer.id)
+
     def test_ex_destroy_balancer_access_rules_no_poll(self):
         balancer = self.driver.get_balancer(balancer_id='94699')
 
@@ -599,6 +606,14 @@ class RackspaceLBTests(unittest.TestCase):
         balancer = self.driver.get_balancer(balancer_id='8290')
         members = balancer.list_members()
 
+        balancer = self.driver.ex_balancer_detach_members(balancer, members)
+
+        self.assertEquals('8290', balancer.id)
+
+    def test_ex_detach_members_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        members = balancer.list_members()
+
         ret = self.driver.ex_balancer_detach_members_no_poll(balancer, members)
         self.assertTrue(ret)
 
-- 
1.7.5.4


From 98e44a29d0d55cc0e927433302e0c290e2ee231b Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Thu, 5 Jan 2012 16:44:56 -0500
Subject: [PATCH 30/32] Use common update pattern for update_member.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 2f8497f..0f6bd85 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -423,27 +423,20 @@ class RackspaceLBDriver(Driver):
         return resp.status == httplib.ACCEPTED
 
     def ex_balancer_update_member(self, balancer, member, **kwargs):
-        resp = self.connection.request(
-            action='/loadbalancers/%s/nodes/%s' % (balancer.id, member.id),
-            method='PUT',
-            data=json.dumps(self._kwargs_to_mutable_member_attrs(**kwargs))
-        )
+        accepted = self.ex_balancer_update_member_no_poll(
+            balancer, member, **kwargs)
 
-        if resp.status != httplib.ACCEPTED:
-            raise LibcloudError('Update member attributes was not accepted')
+        if not accepted:
+            msg = 'Update member attributes was not accepted'
+            raise LibcloudError(msg, driver=self)
 
         # Updating a member puts a balancer into 'PENDING_UPDATE' status.
         # Wait until the balancer is back in 'ACTIVE' status and fetch
         # the updated member.
-        balancer_resp = self.connection.async_request(
-            action='/loadbalancers/%s' % balancer.id,
-            method='GET')
-
-        balancer = self._to_balancer(balancer_resp.object['loadBalancer'])
-        extra_members = balancer.extra['members']
+        balancer = self._get_updated_balancer(balancer)
+        members = balancer.extra['members']
 
-        updated_members = [extra_member for extra_member in extra_members \
-                           if extra_member.id == member.id]
+        updated_members = [m for m in members if m.id == member.id]
 
         if not updated_members:
             raise LibcloudError('Could not find updated member')
-- 
1.7.5.4


From 90c45ecd4ce8ccc69ac4180493ecbb4f1cde8883 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Fri, 6 Jan 2012 07:46:53 -0500
Subject: [PATCH 31/32] Move informative comment elsewhere.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 0f6bd85..7b80cfe 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -430,9 +430,6 @@ class RackspaceLBDriver(Driver):
             msg = 'Update member attributes was not accepted'
             raise LibcloudError(msg, driver=self)
 
-        # Updating a member puts a balancer into 'PENDING_UPDATE' status.
-        # Wait until the balancer is back in 'ACTIVE' status and fetch
-        # the updated member.
         balancer = self._get_updated_balancer(balancer)
         members = balancer.extra['members']
 
@@ -473,6 +470,12 @@ class RackspaceLBDriver(Driver):
         return [self._to_access_rule(el) for el in resp.object["accessList"]]
 
     def _get_updated_balancer(self, balancer):
+        """
+        Updating a balancer's attributes puts a balancer into
+        'PENDING_UPDATE' status.  Wait until the balancer is
+        back in 'ACTIVE' status and then return the individual
+        balancer details call.
+        """
         resp = self.connection.async_request(
             action='/loadbalancers/%s' % balancer.id,
             method='GET')
-- 
1.7.5.4


From 6afb7e5c95654cd1aab6cec840211f5a1be37c39 Mon Sep 17 00:00:00 2001
From: "dave.king" <dave.king@rackspace.com>
Date: Fri, 6 Jan 2012 08:18:56 -0500
Subject: [PATCH 32/32] Update with findings from hitting API.


diff --git a/libcloud/loadbalancer/drivers/rackspace.py b/libcloud/loadbalancer/drivers/rackspace.py
index 7b80cfe..88c5394 100644
--- a/libcloud/loadbalancer/drivers/rackspace.py
+++ b/libcloud/loadbalancer/drivers/rackspace.py
@@ -648,7 +648,9 @@ class RackspaceLBDriver(Driver):
         uri = '/loadbalancers/%s/errorpage' % (balancer.id)
         resp = self.connection.request(uri, method='DELETE')
 
-        return resp.status == httplib.ACCEPTED
+        # Load Balancer API currently returns 200 OK on custom error page
+        # delete.
+        return resp.status == httplib.OK or resp.status == httplib.ACCEPTED
 
     def ex_create_balancer_access_rule(self, balancer, rule):
         accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule)
diff --git a/test/loadbalancer/test_rackspace.py b/test/loadbalancer/test_rackspace.py
index 8c7eda7..a47cd41 100644
--- a/test/loadbalancer/test_rackspace.py
+++ b/test/loadbalancer/test_rackspace.py
@@ -993,7 +993,7 @@ class RackspaceLBMockHttp(MockHttpTestCase):
             body = self.fixtures.load("error_page_default.json")
             return (httplib.OK, body, {}, httplib.responses[httplib.OK])
         elif method == 'DELETE':
-            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+            return (httplib.OK, '', {}, httplib.responses[httplib.OK])
 
         raise NotImplementedError
 
-- 
1.7.5.4

