libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1229390 - in /libcloud/trunk: libcloud/loadbalancer/drivers/ test/loadbalancer/ test/loadbalancer/fixtures/rackspace/
Date Mon, 09 Jan 2012 22:12:15 GMT
Author: tomaz
Date: Mon Jan  9 22:12:15 2012
New Revision: 1229390

URL: http://svn.apache.org/viewvc?rev=1229390&view=rev
Log:
Add a bunch of extension methods to the Rackspace Loadbalancer driver. This
patch has been submitted bye Dave King <dave dot king at mailtrust dot com>
and it's part of LIBCLOUD-140.

Added:
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/error_page_default.json
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json
Modified:
    libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json
    libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json
    libcloud/trunk/test/loadbalancer/test_rackspace.py

Modified: libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py?rev=1229390&r1=1229389&r2=1229390&view=diff
==============================================================================
--- libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py (original)
+++ libcloud/trunk/libcloud/loadbalancer/drivers/rackspace.py Mon Jan  9 22:12:15 2012
@@ -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(Rackspa
         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,11 +161,31 @@ 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))
+
+    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
     DENY = 1
 
+    _RULE_TYPE_STRING_MAP = {
+        ALLOW: 'ALLOW',
+        DENY: 'DENY'
+    }
+
 
 class RackspaceAccessRule(object):
     """
@@ -145,18 +195,33 @@ class RackspaceAccessRule(object):
     @param id: Unique identifier to refer to this rule by.
     @type id: C{str}
 
-    @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}
     """
 
-    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
@@ -223,6 +288,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,
@@ -280,6 +347,23 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_destroy_balancers(self, balancers):
+        """
+        Destroys a list of Balancers (the API supports up to 10).
+
+        @param balancers: A list of Balancers to destroy.
+        @type balancers: C{list}
+
+        @rtype: C{bool}
+        @return: Returns whether the destroy request was accepted.
+        """
+        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 +394,49 @@ class RackspaceLBDriver(Driver):
 
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_detach_members(self, balancer, members):
+        """
+        Detaches a list of members from a balancer (the API supports up to 10).
+        This method blocks until the detach request has been processed and the
+        balancer is in a RUNNING state again.
+
+        @param balancer: The Balancer to detach members from.
+        @type balancer: C{Balancer}
+
+        @param members: A list of Members to detach.
+        @type members: C{list}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        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):
+        """
+        Detaches a list of members from a balancer (the API supports up to 10).
+        This method returns immediately.
+
+        @param balancer: The Balancer to detach members from.
+        @type balancer: C{Balancer}
+
+        @param members: A list of Members to detach.
+        @type members: C{list}
+
+        @rtype: C{bool}
+        @return: Returns whether the detach request was accepted.
+        """
+        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(
@@ -331,6 +458,69 @@ class RackspaceLBDriver(Driver):
                     data=json.dumps(attrs))
         return resp.status == httplib.ACCEPTED
 
+    def ex_balancer_update_member(self, balancer, member, **kwargs):
+        """
+        Updates a Member's extra attributes for a Balancer.  The attributes can
+        include 'weight' or 'condition'.  This method blocks until the update
+        request has been processed and the balancer is in a RUNNING state
+        again.
+
+        @param balancer: Balancer to update the member on.
+        @type balancer: C{Balancer}
+
+        @param **kwargs: New attributes.  Should contain either 'weight'
+        or 'condition'.  'condition' can be set to 'ENABLED', 'DISABLED'.
+        or 'DRAINING'.  'weight' can be set to a positive integer between
+        1 and 100, with a higher weight indicating that the node will receive
+        more traffic (assuming the Balancer is using a weighted algorithm).
+        @type **kwargs: C{dict}
+
+        @rtype: C{Member}
+        @return: Updated Member.
+        """
+        accepted = self.ex_balancer_update_member_no_poll(
+            balancer, member, **kwargs)
+
+        if not accepted:
+            msg = 'Update member attributes was not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        balancer = self._get_updated_balancer(balancer)
+        members = balancer.extra['members']
+
+        updated_members = [m for m in members if m.id == member.id]
+
+        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):
+        """
+        Updates a Member's extra attributes for a Balancer.  The attribute can
+        include 'weight' or 'condition'.  This method returns immediately.
+
+        @param balancer: Balancer to update the member on.
+        @type balancer: C{Balancer}
+
+        @param **kwargs: New attributes.  Should contain either 'weight'
+        or 'condition'.  'condition' can be set to 'ENABLED', 'DISABLED'.
+        or 'DRAINING'.  'weight' can be set to a positive integer between
+        1 and 100, with a higher weight indicating that the node will receive
+        more traffic (assuming the Balancer is using a weighted algorithm).
+        @type **kwargs: C{dict}
+
+        @rtype: C{bool}
+        @return: Returns whether the update request was accepted.
+        """
+        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
@@ -351,6 +541,562 @@ 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')
+
+        return self._to_balancer(resp.object['loadBalancer'])
+
+    def ex_update_balancer_health_monitor(self, balancer, health_monitor):
+        """
+        Sets a Balancer's health monitor.  This method blocks until the update
+        request has been processed and the balancer is in a RUNNING state
+        again.
+
+        @param balancer: Balancer to update.
+        @type balancer: C{Balancer}
+
+        @param health_monitor: Health Monitor for the balancer.
+        @type health_monitor: C{RackspaceHealthMonitor}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        accepted = self.ex_update_balancer_health_monitor_no_poll(balancer,
+                    health_monitor)
+        if not accepted:
+            msg = 'Update health monitor request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_update_balancer_health_monitor_no_poll(self, balancer,
+                                                  health_monitor):
+        """
+        Sets a Balancer's health monitor.  This method returns immediately.
+
+        @param balancer: Balancer to update health monitor on.
+        @type balancer: C{Balancer}
+
+        @param health_monitor: Health Monitor for the balancer.
+        @type health_monitor: C{RackspaceHealthMonitor}
+
+        @rtype: C{bool}
+        @return: Returns whether the update request was accepted.
+        """
+        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_disable_balancer_health_monitor(self, balancer):
+        """
+        Disables a Balancer's health monitor.  This method blocks until the
+        disable request has been processed and the balancer is in a RUNNING
+        state again.
+
+        @param balancer: Balancer to disable health monitor on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_disable_balancer_health_monitor_no_poll(balancer):
+            msg = 'Disable health monitor request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_disable_balancer_health_monitor_no_poll(self, balancer):
+        """
+        Disables a Balancer's health monitor.  This method returns
+        immediately.
+
+        @param balancer: Balancer to disable health monitor on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the disable request was accepted.
+        """
+        uri = '/loadbalancers/%s/healthmonitor' % (balancer.id)
+
+        resp = self.connection.request(uri,
+            method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_update_balancer_connection_throttle(self, balancer,
+                                               connection_throttle):
+        """
+        Updates a Balancer's connection throttle.  This method blocks until
+        the update request has been processed and the balancer is in a
+        RUNNING state again.
+
+        @param balancer: Balancer to update connection throttle on.
+        @type balancer: C{Balancer}
+
+        @param connection_throttle: Connection Throttle for the balancer.
+        @type connection_throttle: C{RackspaceConnectionThrottle}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        accepted = self.ex_update_balancer_connection_throttle_no_poll(
+            balancer, connection_throttle)
+
+        if not accepted:
+            msg = 'Update connection throttle request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_update_balancer_connection_throttle_no_poll(self, balancer,
+                                                       connection_throttle):
+        """
+        Sets a Balancer's connection throttle.  This method returns
+        immediately.
+
+        @param balancer: Balancer to update connection throttle on.
+        @type balancer: C{Balancer}
+
+        @param connection_throttle: Connection Throttle for the balancer.
+        @type connection_throttle: C{RackspaceConnectionThrottle}
+
+        @rtype: C{bool}
+        @return: Returns whether the update request was accepted.
+        """
+        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_disable_balancer_connection_throttle(self, balancer):
+        """
+        Disables a Balancer's connection throttle.  This method blocks until
+        the disable request has been processed and the balancer is in a RUNNING
+        state again.
+
+        @param balancer: Balancer to disable connection throttle on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_disable_balancer_connection_throttle_no_poll(balancer):
+            msg = 'Disable connection throttle request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_disable_balancer_connection_throttle_no_poll(self, balancer):
+        """
+        Disables a Balancer's connection throttle.  This method returns
+        immediately.
+
+        @param balancer: Balancer to disable connection throttle on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the disable request was accepted.
+        """
+        uri = '/loadbalancers/%s/connectionthrottle' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_enable_balancer_connection_logging(self, balancer):
+        """
+        Enables connection logging for a Balancer.  This method blocks until
+        the enable request has been processed and the balancer is in a RUNNING
+        state again.
+
+        @param balancer: Balancer to enable connection logging on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_enable_balancer_connection_logging_no_poll(balancer):
+            msg = 'Enable connection logging request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_enable_balancer_connection_logging_no_poll(self, balancer):
+        """
+        Enables connection logging for a Balancer.  This method returns
+        immediately.
+
+        @param balancer: Balancer to enable connection logging on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the enable request was accepted.
+        """
+        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_disable_balancer_connection_logging(self, balancer):
+        """
+        Disables connection logging for a Balancer.  This method blocks until
+        the enable request has been processed and the balancer is in a RUNNING
+        state again.
+
+        @param balancer: Balancer to disable connection logging on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_disable_balancer_connection_logging_no_poll(balancer):
+            msg = 'Disable connection logging request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_disable_balancer_connection_logging_no_poll(self, balancer):
+        """
+        Disables connection logging for a Balancer.  This method returns
+        immediately.
+
+        @param balancer: Balancer to disable connection logging on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the disable request was accepted.
+        """
+        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 ex_enable_balancer_session_persistence(self, balancer):
+        """
+        Enables session persistence for a Balancer by setting the persistence
+        type to 'HTTP_COOKIE'.  This method blocks until the enable request
+        has been processed and the balancer is in a RUNNING state again.
+
+        @param balancer: Balancer to enable session persistence on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_enable_balancer_session_persistence_no_poll(balancer):
+            msg = 'Enable session persistence request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_enable_balancer_session_persistence_no_poll(self, balancer):
+        """
+        Enables session persistence for a Balancer by setting the persistence
+        type to 'HTTP_COOKIE'.  This method returns immediately.
+
+        @param balancer: Balancer to enable session persistence on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the enable request was accepted.
+        """
+        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_disable_balancer_session_persistence(self, balancer):
+        """
+        Disables session persistence for a Balancer.  This method blocks until
+        the disable request has been processed and the balancer is in a RUNNING
+        state again.
+
+        @param balancer: Balancer to disable session persistence on.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_disable_balancer_session_persistence_no_poll(balancer):
+            msg = 'Disable session persistence request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_disable_balancer_session_persistence_no_poll(self, balancer):
+        """
+        Disables session persistence for a Balancer.  This method returns
+        immediately.
+
+        @param balancer: Balancer to disable session persistence for.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the disable request was accepted.
+        """
+        uri = '/loadbalancers/%s/sessionpersistence' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_update_balancer_error_page(self, balancer, page_content):
+        """
+        Updates a Balancer's custom error page.  This method blocks until
+        the update request has been processed and the balancer is in a
+        RUNNING state again.
+
+        @param balancer: Balancer to update the custom error page for.
+        @type balancer: C{Balancer}
+
+        @param page_content: HTML content for the custom error page.
+        @type page_content: C{string}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        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, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_update_balancer_error_page_no_poll(self, balancer, page_content):
+        """
+        Updates a Balancer's custom error page.  This method returns
+        immediately.
+
+        @param balancer: Balancer to update the custom error page for.
+        @type balancer: C{Balancer}
+
+        @param page_content: HTML content for the custom error page.
+        @type page_content: C{string}
+
+        @rtype: C{bool}
+        @return: Returns whether the update request was accepted.
+        """
+        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_disable_balancer_custom_error_page(self, balancer):
+        """
+        Disables a Balancer's custom error page, returning its error page to
+        the Rackspace-provided default.  This method blocks until the disable
+        request has been processed and the balancer is in a RUNNING state
+        again.
+
+        @param balancer: Balancer to disable the custom error page for.
+        @type balancer: C{Balancer}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        if not self.ex_disable_balancer_custom_error_page_no_poll(balancer):
+            msg = 'Disable custom error page request not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_disable_balancer_custom_error_page_no_poll(self, balancer):
+        """
+        Disables a Balancer's custom error page, returning its error page to
+        the Rackspace-provided default.  This method returns immediately.
+
+        @param balancer: Balancer to disable the custom error page for.
+        @type balancer: C{Balancer}
+
+        @rtype: C{bool}
+        @return: Returns whether the disable request was accepted.
+        """
+        uri = '/loadbalancers/%s/errorpage' % (balancer.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        # 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):
+        """
+        Adds an access rule to a Balancer's access list.  This method blocks
+        until the update request has been processed and the balancer is in a
+        RUNNING state again.
+
+        @param balancer: Balancer to create the access rule for.
+        @type balancer: C{Balancer}
+
+        @param rule: Access Rule to add to the balancer.
+        @type rule: C{RackspaceAccessRule}
+
+        @rtype: C{RackspaceAccessRule}
+        @return: The created access rule.
+        """
+        accepted = self.ex_create_balancer_access_rule_no_poll(balancer, rule)
+        if not accepted:
+            msg = 'Create access rule not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        balancer = self._get_updated_balancer(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 unique, so this is a safe
+        # way to uniquely identify the new access rule.
+        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):
+        """
+        Adds an access rule to a Balancer's access list.  This method returns
+        immediately.
+
+        @param balancer: Balancer to create the access rule for.
+        @type balancer: C{Balancer}
+
+        @param rule: Access Rule to add to the balancer.
+        @type rule: C{RackspaceAccessRule}
+
+        @rtype: C{bool}
+        @return: Returns whether the create request was accepted.
+        """
+        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 ex_destroy_balancer_access_rule(self, balancer, rule):
+        """
+        Removes an access rule from a Balancer's access list.  This method
+        blocks until the update request has been processed and the balancer
+        is in a RUNNING state again.
+
+        @param balancer: Balancer to remove the access rule from.
+        @type balancer: C{Balancer}
+
+        @param rule: Access Rule to remove from the balancer.
+        @type rule: C{RackspaceAccessRule}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        accepted = self.ex_destroy_balancer_access_rule_no_poll(balancer, rule)
+        if not accepted:
+            msg = 'Delete access rule not accepted'
+            raise LibcloudError(msg, driver=self)
+
+        return self._get_updated_balancer(balancer)
+
+    def ex_destroy_balancer_access_rule_no_poll(self, balancer, rule):
+        """
+        Removes an access rule from a Balancer's access list.  This method
+        returns immediately.
+
+        @param balancer: Balancer to remove the access rule from.
+        @type balancer: C{Balancer}
+
+        @param rule: Access Rule to remove from the balancer.
+        @type rule: C{RackspaceAccessRule}
+
+        @rtype: C{bool}
+        @return: Returns whether the destroy request was accepted.
+        """
+        uri = '/loadbalancers/%s/accesslist/%s' % (balancer.id, rule.id)
+        resp = self.connection.request(uri, method='DELETE')
+
+        return resp.status == httplib.ACCEPTED
+
+    def ex_destroy_balancer_access_rules(self, balancer, rules):
+        """
+        Removes a list of access rules from a Balancer's access list.  This
+        method blocks until the update request has been processed and the
+        balancer is in a RUNNING state again.
+
+        @param balancer: Balancer to remove the access rules from.
+        @type balancer: C{Balancer}
+
+        @param rules: List of C{RackspaceAccessRule} objects to remove from the
+        balancer.
+        @type rules: C{list}
+
+        @rtype: C{Balancer}
+        @return: Updated Balancer.
+        """
+        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):
+        """
+        Removes a list of access rules from a Balancer's access list.  This
+        method returns immediately.
+
+        @param balancer: Balancer to remove the access rules from.
+        @type balancer: C{Balancer}
+
+        @param rules: List of C{RackspaceAccessRule} objects to remove from the
+        balancer.
+        @type rules: C{list}
+
+        @rtype: C{bool}
+        @return: Returns whether the destroy request was accepted.
+        """
+        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"]:
@@ -415,6 +1161,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(
@@ -463,6 +1213,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

Added: libcloud/trunk/test/loadbalancer/fixtures/rackspace/error_page_default.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/error_page_default.json?rev=1229390&view=auto
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/error_page_default.json (added)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/error_page_default.json Mon Jan  9 22:12:15 2012
@@ -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

Added: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json?rev=1229390&view=auto
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json (added)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_8290_errorpage.json Mon Jan  9 22:12:15 2012
@@ -0,0 +1 @@
+{"errorpage":{"content":"<html>Generic Error Page</html>"}}
\ No newline at end of file

Modified: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json?rev=1229390&r1=1229389&r2=1229390&view=diff
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json (original)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94695_full_details.json Mon Jan  9 22:12:15 2012
@@ -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

Modified: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json?rev=1229390&r1=1229389&r2=1229390&view=diff
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json (original)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94696_http_health_monitor.json Mon Jan  9 22:12:15 2012
@@ -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

Modified: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json?rev=1229390&r1=1229389&r2=1229390&view=diff
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json (original)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94697_https_health_monitor.json Mon Jan  9 22:12:15 2012
@@ -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}}}

Added: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json?rev=1229390&view=auto
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json (added)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_access_list.json Mon Jan  9 22:12:15 2012
@@ -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

Added: libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json?rev=1229390&view=auto
==============================================================================
--- libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json (added)
+++ libcloud/trunk/test/loadbalancer/fixtures/rackspace/v1_slug_loadbalancers_94698_with_access_list.json Mon Jan  9 22:12:15 2012
@@ -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":{"max
 Connections":200,"minConnections":50,"maxConnectionRate":50,"rateInterval":10},"connectionLogging":{"enabled":true}}}
\ No newline at end of file

Modified: libcloud/trunk/test/loadbalancer/test_rackspace.py
URL: http://svn.apache.org/viewvc/libcloud/trunk/test/loadbalancer/test_rackspace.py?rev=1229390&r1=1229389&r2=1229390&view=diff
==============================================================================
--- libcloud/trunk/test/loadbalancer/test_rackspace.py (original)
+++ libcloud/trunk/test/loadbalancer/test_rackspace.py Mon Jan  9 22:12:15 2012
@@ -23,10 +23,12 @@ except ImportError:
     import json
 
 from libcloud.utils.py3 import httplib
+from libcloud.utils.py3 import urllib
+from libcloud.utils.py3 import urlencode
 
 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, RackspaceConnectionThrottle, RackspaceAccessRule
 from libcloud.loadbalancer.drivers.rackspace import RackspaceUKLBDriver
 from libcloud.loadbalancer.drivers.rackspace import RackspaceAccessRuleType
 from libcloud.common.types import LibcloudError
@@ -108,6 +110,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')
 
@@ -161,6 +168,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)
@@ -258,6 +287,272 @@ 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='94698')
+
+        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='94698')
+
+        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_destroy_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_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_destroy_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_destroy_balancer_access_rule_no_poll(balancer,
+            rule)
+
+        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')
+
+        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,
+            attempts_before_deactivation=2)
+
+        balancer = self.driver.ex_update_balancer_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_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,
+            path='/',
+            status_regex='^[234][0-9][0-9]$',
+            body_regex='Hello World!')
+
+        balancer = self.driver.ex_update_balancer_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_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)
+
+        resp = self.driver.ex_update_balancer_health_monitor_no_poll(balancer,
+            monitor)
+
+        self.assertTrue(resp)
+
+    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,
+            path='/',
+            status_regex='^[234][0-9][0-9]$',
+            body_regex='Hello World!')
+
+        resp = self.driver.ex_update_balancer_health_monitor_no_poll(balancer,
+            monitor)
+
+        self.assertTrue(resp)
+
+    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_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_ex_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_update_balancer_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_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,
+            max_connection_rate=50,
+            rate_interval_seconds=10)
+
+        resp = self.driver.ex_update_balancer_connection_throttle_no_poll(
+            balancer, connection_throttle)
+
+        self.assertTrue(resp)
+
+    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_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_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_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_ex_disable_balancer_connection_logging(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_disable_balancer_connection_logging(
+            balancer
+        )
+
+        self.assertFalse(balancer.extra["connectionLoggingEnabled"])
+
+    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
+        )
+
+        self.assertTrue(resp)
+
+    def test_ex_enable_balancer_session_persistence(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        balancer = self.driver.ex_enable_balancer_session_persistence(balancer)
+
+        persistence_type = balancer.extra['sessionPersistenceType']
+        self.assertEquals('HTTP_COOKIE', persistence_type)
+
+    def test_ex_enable_balancer_session_persistence_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        resp = self.driver.ex_enable_balancer_session_persistence_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
+    def test_disable_balancer_session_persistence(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        balancer = self.driver.ex_disable_balancer_session_persistence(
+            balancer)
+
+        self.assertTrue('sessionPersistenceType' not in balancer.extra)
+
+    def test_disable_balancer_session_persistence_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='8290')
+        resp = self.driver.ex_disable_balancer_session_persistence_no_poll(
+            balancer)
+
+        self.assertTrue(resp)
+
+    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_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_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_update_balancer_error_page_no_poll(
+            balancer, content)
+
+        self.assertTrue(resp)
+
+    def test_ex_disable_balancer_custom_error_page_no_poll(self):
+        balancer = self.driver.get_balancer(balancer_id='94695')
+        resp = self.driver.ex_disable_balancer_custom_error_page_no_poll(balancer)
+
+        self.assertTrue(resp)
+
+    def test_ex_disable_balancer_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_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)
+
     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')
@@ -308,6 +603,21 @@ 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()
+
+        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)
+
     def test_update_balancer_protocol(self):
         balancer = LoadBalancer(id='3130', name='LB_update',
                                          state='PENDING_UPDATE', ip='10.34.4.3',
@@ -397,6 +707,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):
@@ -442,6 +775,16 @@ class RackspaceLBMockHttp(MockHttpTestCa
             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(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 +799,11 @@ class RackspaceLBMockHttp(MockHttpTestCa
         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,15 +813,72 @@ class RackspaceLBMockHttp(MockHttpTestCa
             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(urlencode([('id', id)]) in url,
+                    msg='Did not delete member with id %d' % id)
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
 
         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
 
+    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])
+
+        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)
+
+            self.assertFalse(json_body["connectionLogging"]["enabled"])
+
+            return (httplib.ACCEPTED, '', {}, httplib.responses[httplib.ACCEPTED])
+
+        raise NotImplementedError
+
+    def _v1_0_slug_loadbalancers_8290_sessionpersistence(self, method, url, body, headers):
+        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
+
     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")
@@ -498,8 +901,8 @@ class RackspaceLBMockHttp(MockHttpTestCa
         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])
 
         raise NotImplementedError
@@ -539,6 +942,62 @@ class RackspaceLBMockHttp(MockHttpTestCa
 
         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_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_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_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_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.OK, '', {}, httplib.responses[httplib.OK])
+
+        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 +1005,22 @@ class RackspaceLBMockHttp(MockHttpTestCa
 
         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")
@@ -553,6 +1028,60 @@ class RackspaceLBMockHttp(MockHttpTestCa
 
         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_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_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(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])
+
+        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":



Mime
View raw message