diff --git a/contrib/rackspace/rackspace/resources/cloud_loadbalancer.py b/contrib/rackspace/rackspace/resources/cloud_loadbalancer.py index 945010468f..219bdf4795 100644 --- a/contrib/rackspace/rackspace/resources/cloud_loadbalancer.py +++ b/contrib/rackspace/rackspace/resources/cloud_loadbalancer.py @@ -47,25 +47,6 @@ def lb_immutable(exc): return False -def retry_if_immutable(task): - @six.wraps(task) - def wrapper(*args, **kwargs): - while True: - yield - try: - task(*args, **kwargs) - except Exception as exc: - # InvalidLoadBalancerParameters, or BadRequest for the - # same immutable load balancer error, so check the - # exception message instead of the exception type - if lb_immutable(exc): - continue - raise - else: - break - return wrapper - - class LoadbalancerBuildError(exception.HeatException): msg_fmt = _("There was an error building the loadbalancer:%(lb_name)s.") @@ -463,6 +444,7 @@ class CloudLoadBalancer(resource.Resource): ACTIVE_STATUS = 'ACTIVE' DELETED_STATUS = 'DELETED' PENDING_DELETE_STATUS = 'PENDING_DELETE' + PENDING_UPDATE_STATUS = 'PENDING_UPDATE' def __init__(self, name, json_snippet, stack): super(CloudLoadBalancer, self).__init__(name, json_snippet, stack) @@ -637,71 +619,131 @@ class CloudLoadBalancer(resource.Resource): "(was: %s)") % loadbalancer.status) def handle_update(self, json_snippet, tmpl_diff, prop_diff): - """Add and remove nodes specified in the prop_diff.""" + return prop_diff + + def check_update_complete(self, prop_diff): lb = self.clb.get(self.resource_id) - checkers = [] + return (lb.status != self.PENDING_UPDATE_STATUS and # lb immutable? + self._update_props(lb, prop_diff) and + self._update_nodes_add(lb, prop_diff) and + self._update_nodes_delete(lb, prop_diff) and + self._update_nodes_change(lb, prop_diff) and + self._update_health_monitor(lb, prop_diff) and + self._update_session_persistence(lb, prop_diff) and + self._update_ssl_termination(lb, prop_diff) and + self._update_metadata(lb, prop_diff) and + self._update_errorpage(lb, prop_diff) and + self._update_connection_logging(lb, prop_diff) and + self._update_connection_throttle(lb, prop_diff) and + self._update_content_caching(lb, prop_diff)) - if self.NODES in prop_diff: - updated_nodes = prop_diff[self.NODES] - checkers.extend(self._update_nodes(lb, updated_nodes)) + def _nodes_need_update_add(self, old, new): + if not old: + return True + + new = list(self._process_nodes(new)) + new_nodes = ["%s%s" % (x['address'], x['port']) for x in new] + old_nodes = ["%s%s" % (x.address, x.port) for x in old] + for node in new_nodes: + if node not in old_nodes: + return True + + return False + + def _nodes_need_update_delete(self, old, new): + if not new: + return True + + new = list(self._process_nodes(new)) + new_nodes = ["%s%s" % (x['address'], x['port']) for x in new] + old_nodes = ["%s%s" % (x.address, x.port) for x in old] + for node in old_nodes: + if node not in new_nodes: + return True + + return False + + def _nodes_need_update_change(self, old, new): + def find_node(nodes, address, port): + for node in nodes: + if node['address'] == address and node['port'] == port: + return node + + new = list(self._process_nodes(new)) + for old_node in old: + new_node = find_node(new, old_node.address, old_node.port) + if (new_node['condition'] != old_node.condition or + new_node['type'] != old_node.type or + new_node['weight'] != old_node.weight): + return True + + return False + + def _needs_update_comparison(self, old, new): + if old != new: + return True + return False + + def _needs_update_comparison_bool(self, old, new): + if new is None: + return old + return self._needs_update_comparison(old, new) + + def _needs_update_comparison_nullable(self, old, new): + if not old and not new: + return False + return self._needs_update_comparison(old, new) + + def _props_need_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # dict + + def _hm_needs_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # dict + + def _sp_needs_update(self, old, new): + return self._needs_update_comparison_bool(old, new) # bool + + def _metadata_needs_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # dict + + def _errorpage_needs_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # str + + def _cl_needs_update(self, old, new): + return self._needs_update_comparison_bool(old, new) # bool + + def _ct_needs_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # dict + + def _cc_needs_update(self, old, new): + return self._needs_update_comparison_bool(old, new) # bool + + def _ssl_term_needs_update(self, old, new): + return self._needs_update_comparison_nullable(old, new) # dict + + def _update_props(self, lb, prop_diff): + old_props = {} + new_props = {} - updated_props = {} for prop in six.iterkeys(prop_diff): if prop in self.LB_UPDATE_PROPS: - updated_props[prop] = prop_diff[prop] - if updated_props: - checkers.append(self._update_lb_properties(lb, updated_props)) + old_props[prop] = getattr(lb, prop) + new_props[prop] = prop_diff[prop] - if self.HEALTH_MONITOR in prop_diff: - updated_hm = prop_diff[self.HEALTH_MONITOR] - checkers.append(self._update_health_monitor(lb, updated_hm)) + if new_props and self._props_need_update(old_props, new_props): + try: + lb.update(**new_props) + except Exception as exc: + if lb_immutable(exc): + return False + raise + return False - if self.SESSION_PERSISTENCE in prop_diff: - updated_sp = prop_diff[self.SESSION_PERSISTENCE] - checkers.append(self._update_session_persistence(lb, updated_sp)) + return True - if self.SSL_TERMINATION in prop_diff: - updated_ssl_term = prop_diff[self.SSL_TERMINATION] - checkers.append(self._update_ssl_termination(lb, updated_ssl_term)) - - if self.METADATA in prop_diff: - updated_metadata = prop_diff[self.METADATA] - checkers.append(self._update_metadata(lb, updated_metadata)) - - if self.ERROR_PAGE in prop_diff: - updated_errorpage = prop_diff[self.ERROR_PAGE] - checkers.append(self._update_errorpage(lb, updated_errorpage)) - - if self.CONNECTION_LOGGING in prop_diff: - updated_cl = prop_diff[self.CONNECTION_LOGGING] - checkers.append(self._update_connection_logging(lb, updated_cl)) - - if self.CONNECTION_THROTTLE in prop_diff: - updated_ct = prop_diff[self.CONNECTION_THROTTLE] - checkers.append(self._update_connection_throttle(lb, updated_ct)) - - if self.CONTENT_CACHING in prop_diff: - updated_cc = prop_diff[self.CONTENT_CACHING] - checkers.append(self._update_content_caching(lb, updated_cc)) - - return checkers - - def _update_nodes(self, lb, updated_nodes): - @retry_if_immutable - def add_nodes(lb, new_nodes): - lb.add_nodes(new_nodes) - - @retry_if_immutable - def remove_node(known, node): - known[node].delete() - - @retry_if_immutable - def update_node(known, node): - known[node].update() - - checkers = [] + def _nodes_update_data(self, lb, prop_diff): current_nodes = lb.nodes - diff_nodes = self._process_nodes(updated_nodes) + diff_nodes = self._process_nodes(prop_diff[self.NODES]) # Loadbalancers can be uniquely identified by address and # port. Old is a dict of all nodes the loadbalancer # currently knows about. @@ -720,24 +762,66 @@ class CloudLoadBalancer(resource.Resource): added = new_set.difference(old_set) updated = new_set.intersection(old_set) - if len(current_nodes) + len(added) - len(deleted) < 1: - raise ValueError(_("The loadbalancer:%s requires at least one " - "node.") % self.name) - """ - Add loadbalancers in the new map that are not in the old map. - Add before delete to avoid deleting the last node and getting in - an invalid state. - """ + return old, new, deleted, added, updated + + def _update_nodes_add(self, lb, prop_diff): + """Add loadbalancers in the new map that are not in the old map.""" + if self.NODES not in prop_diff: + return True + + old_nodes = lb.nodes if hasattr(lb, self.NODES) else None + new_nodes = prop_diff[self.NODES] + if not self._nodes_need_update_add(old_nodes, new_nodes): + return True + + old, new, deleted, added, updated = self._nodes_update_data(lb, + prop_diff) new_nodes = [self.clb.Node(**new[lb_node]) for lb_node in added] if new_nodes: - checkers.append(scheduler.TaskRunner(add_nodes, lb, new_nodes)) + try: + lb.add_nodes(new_nodes) + except Exception as exc: + if lb_immutable(exc): + return False + raise - # Delete loadbalancers in the old dict that are not in the - # new dict. + return False + + def _update_nodes_delete(self, lb, prop_diff): + """Delete loadbalancers in the old dict that aren't in the new dict.""" + if self.NODES not in prop_diff: + return True + + old_nodes = lb.nodes if hasattr(lb, self.NODES) else None + new_nodes = prop_diff[self.NODES] + if not self._nodes_need_update_delete(old_nodes, new_nodes): + return True + + old, new, deleted, added, updated = self._nodes_update_data(lb, + prop_diff) for node in deleted: - checkers.append(scheduler.TaskRunner(remove_node, old, node)) + try: + old[node].delete() + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_nodes_change(self, lb, prop_diff): + """Update nodes that have been changed.""" + if self.NODES not in prop_diff: + return True + + old_nodes = lb.nodes if hasattr(lb, self.NODES) else None + new_nodes = prop_diff[self.NODES] + if not self._nodes_need_update_change(old_nodes, new_nodes): + return True + + old, new, deleted, added, updated = self._nodes_update_data(lb, + prop_diff) - # Update nodes that have been changed for node in updated: node_changed = False for attribute in six.iterkeys(new[node]): @@ -746,141 +830,183 @@ class CloudLoadBalancer(resource.Resource): node_changed = True setattr(old[node], attribute, new_value) if node_changed: - checkers.append(scheduler.TaskRunner(update_node, old, node)) + try: + old[node].update() + except Exception as exc: + if lb_immutable(exc): + return False + raise - return checkers + return False - def _update_lb_properties(self, lb, updated_props): - @retry_if_immutable - def update_lb(): - lb.update(**updated_props) + def _update_health_monitor(self, lb, prop_diff): + if self.HEALTH_MONITOR not in prop_diff: + return True - return scheduler.TaskRunner(update_lb) + old_hm = lb.get_health_monitor() + new_hm = prop_diff[self.HEALTH_MONITOR] + if not self._hm_needs_update(old_hm, new_hm): + return True - def _update_health_monitor(self, lb, updated_hm): - @retry_if_immutable - def add_health_monitor(): - lb.add_health_monitor(**updated_hm) - - @retry_if_immutable - def delete_health_monitor(): - lb.delete_health_monitor() - - if updated_hm is None: - return scheduler.TaskRunner(delete_health_monitor) - else: - # Adding a health monitor is a destructive, so there's - # no need to delete, then add - return scheduler.TaskRunner(add_health_monitor) - - def _update_session_persistence(self, lb, updated_sp): - @retry_if_immutable - def add_session_persistence(): - lb.session_persistence = updated_sp - - @retry_if_immutable - def delete_session_persistence(): - lb.session_persistence = '' - - if updated_sp is None: - return scheduler.TaskRunner(delete_session_persistence) - else: - # Adding session persistence is destructive - return scheduler.TaskRunner(add_session_persistence) - - def _update_ssl_termination(self, lb, updated_ssl_term): - @retry_if_immutable - def add_ssl_termination(): - lb.add_ssl_termination(**updated_ssl_term) - - @retry_if_immutable - def delete_ssl_termination(): - lb.delete_ssl_termination() - - if updated_ssl_term is None: - return scheduler.TaskRunner(delete_ssl_termination) - else: - # Adding SSL termination is destructive - return scheduler.TaskRunner(add_ssl_termination) - - def _update_metadata(self, lb, updated_metadata): - @retry_if_immutable - def add_metadata(): - lb.set_metadata(updated_metadata) - - @retry_if_immutable - def delete_metadata(): - lb.delete_metadata() - - if updated_metadata is None: - return scheduler.TaskRunner(delete_metadata) - else: - return scheduler.TaskRunner(add_metadata) - - def _update_errorpage(self, lb, updated_errorpage): - @retry_if_immutable - def add_errorpage(): - lb.set_error_page(updated_errorpage) - - @retry_if_immutable - def delete_errorpage(): - lb.clear_error_page() - - if updated_errorpage is None: - return scheduler.TaskRunner(delete_errorpage) - else: - return scheduler.TaskRunner(add_errorpage) - - def _update_connection_logging(self, lb, updated_cl): - @retry_if_immutable - def enable_connection_logging(): - lb.connection_logging = True - - @retry_if_immutable - def disable_connection_logging(): - lb.connection_logging = False - - if updated_cl: - return scheduler.TaskRunner(enable_connection_logging) - else: - return scheduler.TaskRunner(disable_connection_logging) - - def _update_connection_throttle(self, lb, updated_ct): - @retry_if_immutable - def add_connection_throttle(): - lb.add_connection_throttle(**updated_ct) - - @retry_if_immutable - def delete_connection_throttle(): - lb.delete_connection_throttle() - - if updated_ct is None: - return scheduler.TaskRunner(delete_connection_throttle) - else: - return scheduler.TaskRunner(add_connection_throttle) - - def _update_content_caching(self, lb, updated_cc): - @retry_if_immutable - def enable_content_caching(): - lb.content_caching = True - - @retry_if_immutable - def disable_content_caching(): - lb.content_caching = False - - if updated_cc == 'ENABLED': - return scheduler.TaskRunner(enable_content_caching) - else: - return scheduler.TaskRunner(disable_content_caching) - - def check_update_complete(self, checkers): - """Push all checkers to completion in list order.""" - for checker in checkers: - if not checker.started(): - checker.start() - if not checker.step(): + try: + if new_hm is None: + lb.delete_health_monitor() + else: + # Adding a health monitor is a destructive, so there's + # no need to delete, then add + lb.add_health_monitor(**new_hm) + except Exception as exc: + if lb_immutable(exc): return False - return True + raise + + return False + + def _update_session_persistence(self, lb, prop_diff): + if self.SESSION_PERSISTENCE not in prop_diff: + return True + + old_sp = lb.session_persistence + new_sp = prop_diff[self.SESSION_PERSISTENCE] + if not self._sp_needs_update(old_sp, new_sp): + return True + + try: + if new_sp is None: + lb.session_persistence = '' + else: + # Adding session persistence is destructive + lb.session_persistence = new_sp + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_ssl_termination(self, lb, prop_diff): + if self.SSL_TERMINATION not in prop_diff: + return True + + old_ssl_term = lb.get_ssl_termination() + new_ssl_term = prop_diff[self.SSL_TERMINATION] + if not self._ssl_term_needs_update(old_ssl_term, new_ssl_term): + return True + + try: + if new_ssl_term is None: + lb.delete_ssl_termination() + else: + # Adding SSL termination is destructive + lb.add_ssl_termination(**new_ssl_term) + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_metadata(self, lb, prop_diff): + if self.METADATA not in prop_diff: + return True + + old_metadata = lb.get_metadata() + new_metadata = prop_diff[self.METADATA] + if not self._metadata_needs_update(old_metadata, new_metadata): + return True + + try: + if new_metadata is None: + lb.delete_metadata() + else: + lb.set_metadata(new_metadata) + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_errorpage(self, lb, prop_diff): + if self.ERROR_PAGE not in prop_diff: + return True + + old_errorpage = lb.get_error_page()['errorpage']['content'] + new_errorpage = prop_diff[self.ERROR_PAGE] + if not self._errorpage_needs_update(old_errorpage, new_errorpage): + return True + + try: + if new_errorpage is None: + lb.clear_error_page() + else: + lb.set_error_page(new_errorpage) + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_connection_logging(self, lb, prop_diff): + if self.CONNECTION_LOGGING not in prop_diff: + return True + + old_cl = lb.connection_logging + new_cl = prop_diff[self.CONNECTION_LOGGING] + if not self._cl_needs_update(old_cl, new_cl): + return True + + try: + if new_cl: + lb.connection_logging = True + else: + lb.connection_logging = False + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_connection_throttle(self, lb, prop_diff): + if self.CONNECTION_THROTTLE not in prop_diff: + return True + + old_ct = lb.get_connection_throttle() + new_ct = prop_diff[self.CONNECTION_THROTTLE] + if not self._ct_needs_update(old_ct, new_ct): + return True + + try: + if new_ct is None: + lb.delete_connection_throttle() + else: + lb.add_connection_throttle(**new_ct) + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False + + def _update_content_caching(self, lb, prop_diff): + if self.CONTENT_CACHING not in prop_diff: + return True + + old_cc = lb.content_caching + new_cc = prop_diff[self.CONTENT_CACHING] == 'ENABLED' + if not self._cc_needs_update(old_cc, new_cc): + return True + + try: + lb.content_caching = new_cc + except Exception as exc: + if lb_immutable(exc): + return False + raise + + return False def check_delete_complete(self, *args): if self.resource_id is None: diff --git a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py index d194c7b429..b3492e3149 100644 --- a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py +++ b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py @@ -225,6 +225,15 @@ class FakeLoadBalancer(object): self.Node = FakeNode self.VirtualIP = FakeVirtualIP self.nodes = [] + self.algorithm = "ROUND_ROBIN" + self.session_persistence = "HTTP_COOKIE" + self.connection_logging = False + self.timeout = None + self.httpsRedirect = False + self.protocol = None + self.port = None + self.name = None + self.halfClosed = None def get(self, *args, **kwargs): pass @@ -271,6 +280,21 @@ class FakeLoadBalancer(object): def delete(self, *args, **kwargs): pass + def get_health_monitor(self, *args, **kwargs): + return {} + + def get_metadata(self, *args, **kwargs): + return {} + + def get_error_page(self, *args, **kwargs): + pass + + def get_connection_throttle(self, *args, **kwargs): + pass + + def get_ssl_termination(self, *args, **kwargs): + pass + class LoadBalancerWithFakeClient(lb.CloudLoadBalancer): def cloud_lb(self): @@ -354,17 +378,17 @@ class LoadBalancerTest(common.HeatTestCase): resource_defns[resource_name], stack) - fake_loadbalancer = FakeLoadBalancer(name=lb_name) - fake_loadbalancer.status = 'ACTIVE' + fake_lb = FakeLoadBalancer(name=lb_name) + fake_lb.status = 'ACTIVE' self.m.StubOutWithMock(rsrc.clb, 'create') - rsrc.clb.create(lb_name, **lb_body).AndReturn(fake_loadbalancer) + rsrc.clb.create(lb_name, **lb_body).AndReturn(fake_lb) self.m.StubOutWithMock(rsrc.clb, 'get') rsrc.clb.get(mox.IgnoreArg()).MultipleTimes().AndReturn( - fake_loadbalancer) + fake_lb) - return (rsrc, fake_loadbalancer) + return (rsrc, fake_lb) def _get_first_resource_name(self, templ): return next(k for k in templ['Resources']) @@ -373,13 +397,13 @@ class LoadBalancerTest(common.HeatTestCase): t = template_format.parse(json.dumps(lb_template)) self.stack = utils.parse_stack(t, stack_name=utils.random_name()) - rsrc, fake_loadbalancer = self._mock_create(self.stack.t, self.stack, - self. - _get_first_resource_name( - lb_template), - expected_name, - expected_body) - return (rsrc, fake_loadbalancer) + rsrc, fake_lb = self._mock_create(self.stack.t, self.stack, + self. + _get_first_resource_name( + lb_template), + expected_name, + expected_body) + return (rsrc, fake_lb) def _set_template(self, templ, **kwargs): for k, v in six.iteritems(kwargs): @@ -396,9 +420,9 @@ class LoadBalancerTest(common.HeatTestCase): nodes = [{'addresses': ['1234'], 'port': 80, 'enabled': True}, {'addresses': ['4567', '8901', '8903'], 'port': 80, 'enabled': True}] - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) expected_nodes = [{'address': '1234', 'port': 80, 'enabled': True}, {'address': '4567', 'port': 80, 'enabled': True}, {'address': '8901', 'port': 80, 'enabled': True}, @@ -411,7 +435,7 @@ class LoadBalancerTest(common.HeatTestCase): nodes=[]) expected_body = copy.deepcopy(self.expected_body) expected_body['nodes'] = [] - rsrc, fake_loadbalancer = self._mock_loadbalancer( + rsrc, fake_lb = self._mock_loadbalancer( template, self.lb_name, expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -432,9 +456,9 @@ class LoadBalancerTest(common.HeatTestCase): {'key': 'yolo', 'value': 'heeyyy_gurl'}]) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -473,9 +497,9 @@ class LoadBalancerTest(common.HeatTestCase): # test failure (invalid protocol) template = self._set_template(self.lb_template, halfClosed=True) expected = self._set_expected(self.expected_body, halfClosed=True) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) exc = self.assertRaises(exception.StackValidationFailed, rsrc.validate) self.assertIn('The halfClosed property is only available for the TCP' @@ -484,9 +508,9 @@ class LoadBalancerTest(common.HeatTestCase): # test TCP protocol template = self._set_template(template, protocol='TCP') expected = self._set_expected(expected, protocol='TCP') - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.assertIsNone(rsrc.validate()) # test TCP_CLIENT_FIRST protocol @@ -494,9 +518,9 @@ class LoadBalancerTest(common.HeatTestCase): protocol='TCP_CLIENT_FIRST') expected = self._set_expected(expected, protocol='TCP_CLIENT_FIRST') - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.assertIsNone(rsrc.validate()) def test_validate_health_monitor(self): @@ -511,9 +535,9 @@ class LoadBalancerTest(common.HeatTestCase): healthMonitor=health_monitor) expected = self._set_expected(self.expected_body, healthMonitor=health_monitor) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.assertIsNone(rsrc.validate()) @@ -524,9 +548,9 @@ class LoadBalancerTest(common.HeatTestCase): healthMonitor=health_monitor) expected = self._set_expected(expected, healthMonitor=health_monitor) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) exc = self.assertRaises(exception.StackValidationFailed, rsrc.validate) self.assertIn('Unknown Property bodyRegex', str(exc)) @@ -542,9 +566,9 @@ class LoadBalancerTest(common.HeatTestCase): healthMonitor=health_monitor) expected = self._set_expected(expected, healthMonitor=health_monitor) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.assertIsNone(rsrc.validate()) def test_validate_ssl_termination(self): @@ -559,9 +583,9 @@ class LoadBalancerTest(common.HeatTestCase): sslTermination=ssl_termination) expected = self._set_expected(self.expected_body, sslTermination=ssl_termination) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) exc = self.assertRaises(exception.StackValidationFailed, rsrc.validate) self.assertIn("Property certificate not assigned", six.text_type(exc)) @@ -571,9 +595,9 @@ class LoadBalancerTest(common.HeatTestCase): sslTermination=ssl_termination) expected = self._set_expected(expected, sslTermination=ssl_termination) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.assertIsNone(rsrc.validate()) def test_post_creation_access_list(self): @@ -584,11 +608,11 @@ class LoadBalancerTest(common.HeatTestCase): template = self._set_template(self.lb_template, accessList=access_list) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) - self.m.StubOutWithMock(fake_loadbalancer, 'add_access_list') - fake_loadbalancer.add_access_list(access_list) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) + self.m.StubOutWithMock(fake_lb, 'add_access_list') + fake_lb.add_access_list(access_list) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -597,9 +621,9 @@ class LoadBalancerTest(common.HeatTestCase): def test_ref_id(self): """The Reference ID of the resource is the resource ID.""" template = self._set_template(self.lb_template) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() self.m.VerifyAll() @@ -611,11 +635,11 @@ class LoadBalancerTest(common.HeatTestCase): template = self._set_template(self.lb_template, errorPage=error_page) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) - self.m.StubOutWithMock(fake_loadbalancer, 'set_error_page') - fake_loadbalancer.set_error_page(error_page) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) + self.m.StubOutWithMock(fake_lb, 'set_error_page') + fake_lb.set_error_page(error_page) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -632,11 +656,11 @@ class LoadBalancerTest(common.HeatTestCase): template = self._set_template(self.lb_template, sslTermination=ssl_termination) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) - self.m.StubOutWithMock(fake_loadbalancer, 'add_ssl_termination') - fake_loadbalancer.add_ssl_termination( + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) + self.m.StubOutWithMock(fake_lb, 'add_ssl_termination') + fake_lb.add_ssl_termination( ssl_termination['securePort'], ssl_termination['privatekey'], ssl_termination['certificate'], @@ -688,10 +712,10 @@ class LoadBalancerTest(common.HeatTestCase): self.assertIn('boom', str(exc)) def test_update_add_node_by_address(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) - fake_loadbalancer.nodes = self.expected_body['nodes'] + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) + fake_lb.nodes = self.expected_body['nodes'] self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -705,31 +729,27 @@ class LoadBalancerTest(common.HeatTestCase): "port": 80, "condition": "ENABLED"}] - self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes') - fake_loadbalancer.add_nodes([ - fake_loadbalancer.Node(address=expected_ip, - port=80, - condition='ENABLED')]) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.nodes = [ + FakeNode(address=u"172.168.1.4", port=80, condition=u"ENABLED"), + FakeNode(address=u"166.78.103.141", port=80, condition=u"ENABLED"), + ] + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'add_nodes') + fake_lb.add_nodes([ + fake_lb.Node(address=expected_ip, + port=80, + condition='ENABLED')]) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) self.m.VerifyAll() - def test_update_delete_node_failed(self): - deleted_node = {'nodes': []} - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) - fake_loadbalancer.nodes = self.expected_body['nodes'] - self.m.ReplayAll() - scheduler.TaskRunner(rsrc.create)() - self.m.VerifyAll() - - self.m.ReplayAll() - self.assertRaises(ValueError, rsrc.handle_update, {}, {}, deleted_node) - self.m.VerifyAll() - def test_resolve_attr_noid(self): stack = mock.Mock() stack.db_resource_get.return_value = None @@ -739,13 +759,13 @@ class LoadBalancerTest(common.HeatTestCase): self.assertIsNone(lbres._resolve_attribute("PublicIp")) def test_resolve_attr_virtualips(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) - fake_loadbalancer.virtual_ips = [FakeVirtualIP(address='1.2.3.4', - type='PUBLIC', - ipVersion="IPv6", - id='test-id')] + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) + fake_lb.virtual_ips = [FakeVirtualIP(address='1.2.3.4', + type='PUBLIC', + ipVersion="IPv6", + id='test-id')] self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() expected = [{ @@ -758,15 +778,16 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_nodes_immutable(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) current_nodes = [ FakeNode(address=u"1.1.1.1", port=80, condition=u"ENABLED"), FakeNode(address=u"2.2.2.2", port=80, condition=u"ENABLED"), FakeNode(address=u"3.3.3.3", port=80, condition=u"ENABLED"), ] - fake_loadbalancer.nodes = current_nodes + fake_lb.nodes = current_nodes + fake_lb.tracker = "fake_lb" self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -778,46 +799,112 @@ class LoadBalancerTest(common.HeatTestCase): {"addresses": [expected_ip], "port": 80, "condition": "ENABLED"}, ] - msg = ("Load Balancer is immutable. Status: 'PENDING_UPDATE'") - exc = Exception(msg) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.status = "PENDING_UPDATE" + fake_lb1.tracker = "fake_lb1" + + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) # ACTIVE # Add node `expected_ip` - new_nodes = [fake_loadbalancer.Node(address=expected_ip, port=80, - condition='ENABLED')] - self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes') - fake_loadbalancer.add_nodes(new_nodes).AndRaise(exc) - fake_loadbalancer.add_nodes(new_nodes).AndReturn(None) + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) # PENDING_UPDATE - # Update node 2.2.2.2 - self.m.StubOutWithMock(current_nodes[1], 'update') - current_nodes[1].update().AndRaise(exc) - current_nodes[1].update().AndReturn(None) + fake_lb2 = copy.deepcopy(fake_lb1) + fake_lb2.status = "ACTIVE" + fake_lb2.nodes = [ + FakeNode(address=u"1.1.1.1", port=80, condition=u"ENABLED"), + FakeNode(address=u"2.2.2.2", port=80, condition=u"ENABLED"), + FakeNode(address=u"3.3.3.3", port=80, condition=u"ENABLED"), + FakeNode(address=u"4.4.4.4", port=80, condition=u"ENABLED"), + ] + fake_lb2.tracker = "fake_lb2" + + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) # ACTIVE # Delete node 3.3.3.3 - self.m.StubOutWithMock(current_nodes[2], 'delete') - current_nodes[2].delete().AndRaise(exc) - current_nodes[2].delete().AndReturn(None) + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) # PENDING_UPDATE + + fake_lb3 = copy.deepcopy(fake_lb2) + fake_lb3.status = "ACTIVE" + fake_lb3.nodes = [ + FakeNode(address=u"1.1.1.1", port=80, condition=u"ENABLED"), + FakeNode(address=u"2.2.2.2", port=80, condition=u"ENABLED"), + FakeNode(address=u"4.4.4.4", port=80, condition=u"ENABLED"), + ] + fake_lb3.tracker = "fake_lb3" + + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb3) # ACTIVE + + # Update node 2.2.2.2 + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) # PENDING_UPDATE + + fake_lb4 = copy.deepcopy(fake_lb3) + fake_lb4.status = "ACTIVE" + fake_lb4.nodes = [ + FakeNode(address=u"1.1.1.1", port=80, condition=u"ENABLED"), + FakeNode(address=u"2.2.2.2", port=80, condition=u"DISABLED"), + FakeNode(address=u"4.4.4.4", port=80, condition=u"ENABLED"), + ] + fake_lb4.tracker = "fake_lb4" + + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb4) # ACTIVE self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) self.m.VerifyAll() - def test_update_immutable(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + def test_update_pending_update_status(self): + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['name'] = "updated_name" - self.m.StubOutWithMock(fake_loadbalancer, 'update') + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.name = "updated_name" + fake_lb1.status = "PENDING_UPDATE" # lb is immutable + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.name = "updated_name" + fake_lb2.status = "ACTIVE" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) + + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.update, update_template)() + self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_update_immutable_exception(self): + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['name'] = "updated_name" + + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) # initial iteration + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) # immutable + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.name = "updated_name" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) # after update + + self.m.StubOutWithMock(fake_lb, 'update') msg = ("Load Balancer '%s' has a status of 'PENDING_UPDATE' and " "is considered immutable." % rsrc.resource_id) - fake_loadbalancer.update(name="updated_name").AndRaise(Exception(msg)) - fake_loadbalancer.update(name="updated_name").AndReturn(None) + fake_lb.update(name="updated_name").AndRaise(Exception(msg)) + fake_lb.update(name="updated_name").AndReturn(None) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -825,17 +912,24 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_name(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['name'] = "updated_name" - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(name="updated_name") + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.name = "updated_name" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(name="updated_name") self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -843,9 +937,9 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_multiple(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -853,8 +947,19 @@ class LoadBalancerTest(common.HeatTestCase): update_template['Properties']['name'] = "updated_name" update_template['Properties']['algorithm'] = "RANDOM" - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(name="updated_name", algorithm="RANDOM") + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.name = "updated_name" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.algorithm = "RANDOM" + fake_lb2.name = "updated_name" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(name="updated_name", algorithm="RANDOM") self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -862,17 +967,27 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_algorithm(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['algorithm'] = "RANDOM" - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(algorithm="RANDOM") + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.algorithm = "ROUND_ROBIN" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb1, 'update') + fake_lb1.update(algorithm="RANDOM") + + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.algorithm = "RANDOM" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -880,17 +995,24 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_protocol(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['protocol'] = "IMAPS" - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(protocol="IMAPS") + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.protocol = "IMAPS" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(protocol="IMAPS") self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -904,17 +1026,24 @@ class LoadBalancerTest(common.HeatTestCase): expected = self._set_expected( self.expected_body, protocol="HTTPS") - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['httpsRedirect'] = True - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(httpsRedirect=True) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.httpsRedirect = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(httpsRedirect=True) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -928,9 +1057,9 @@ class LoadBalancerTest(common.HeatTestCase): expected = self._set_expected( self.expected_body, protocol="HTTPS", httpsRedirect=True) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) @@ -951,26 +1080,33 @@ class LoadBalancerTest(common.HeatTestCase): expected = self._set_expected( self.expected_body, protocol="HTTP", httpsRedirect=False) - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.m.VerifyAll() def test_update_lb_half_closed(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['halfClosed'] = True - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(halfClosed=True) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.halfClosed = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(halfClosed=True) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -978,17 +1114,24 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_port(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['port'] = 1234 - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(port=1234) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.port = 1234 + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(port=1234) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -996,17 +1139,24 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_lb_timeout(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['timeout'] = 120 - self.m.StubOutWithMock(fake_loadbalancer, 'update') - fake_loadbalancer.update(timeout=120) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb) + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.timeout = 120 + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb, 'update') + fake_lb.update(timeout=120) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -1014,9 +1164,9 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_health_monitor_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1027,8 +1177,16 @@ class LoadBalancerTest(common.HeatTestCase): 'statusRegex': "^[234][0-9][0-9]$", 'bodyRegex': ".* testing .*", 'hostHeader': "example.com"} - self.m.StubOutWithMock(fake_loadbalancer, 'add_health_monitor') - fake_loadbalancer.add_health_monitor( + self.m.StubOutWithMock(fake_lb, 'get_health_monitor') + fake_lb.get_health_monitor().AndReturn({}) + fake_lb.get_health_monitor().AndReturn( + {'type': "HTTP", 'delay': 10, 'timeout': 10, + 'attemptsBeforeDeactivation': 4, 'path': "/", + 'statusRegex': "^[234][0-9][0-9]$", 'bodyRegex': ".* testing .*", + 'hostHeader': "example.com"}) + + self.m.StubOutWithMock(fake_lb, 'add_health_monitor') + fake_lb.add_health_monitor( attemptsBeforeDeactivation=4, bodyRegex='.* testing .*', delay=10, hostHeader='example.com', path='/', statusRegex='^[234][0-9][0-9]$', timeout=10, type='HTTP') @@ -1048,9 +1206,9 @@ class LoadBalancerTest(common.HeatTestCase): template['Resources'][lb_name]['Properties']['healthMonitor'] = hm expected_body = copy.deepcopy(self.expected_body) expected_body['healthMonitor'] = hm - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1058,8 +1216,16 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['healthMonitor'] - self.m.StubOutWithMock(fake_loadbalancer, 'delete_health_monitor') - fake_loadbalancer.delete_health_monitor() + self.m.StubOutWithMock(fake_lb, 'get_health_monitor') + fake_lb.get_health_monitor().AndReturn( + {'type': "HTTP", 'delay': 10, 'timeout': 10, + 'attemptsBeforeDeactivation': 4, 'path': "/", + 'statusRegex': "^[234][0-9][0-9]$", 'bodyRegex': ".* testing .*", + 'hostHeader': "example.com"}) + fake_lb.get_health_monitor().AndReturn({}) + + self.m.StubOutWithMock(fake_lb, 'delete_health_monitor') + fake_lb.delete_health_monitor() self.m.ReplayAll() @@ -1068,9 +1234,9 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_session_persistence_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1081,7 +1247,7 @@ class LoadBalancerTest(common.HeatTestCase): scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual('SOURCE_IP', fake_loadbalancer.session_persistence) + self.assertEqual('SOURCE_IP', fake_lb.session_persistence) self.m.VerifyAll() def test_update_session_persistence_delete(self): @@ -1091,9 +1257,9 @@ class LoadBalancerTest(common.HeatTestCase): 'sessionPersistence'] = "SOURCE_IP" expected_body = copy.deepcopy(self.expected_body) expected_body['sessionPersistence'] = {'persistenceType': "SOURCE_IP"} - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1105,25 +1271,31 @@ class LoadBalancerTest(common.HeatTestCase): scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual('', fake_loadbalancer.session_persistence) + self.assertEqual('', fake_lb.session_persistence) self.m.VerifyAll() def test_update_ssl_termination_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['sslTermination'] = { 'securePort': 443, 'privatekey': private_key, 'certificate': cert, - 'secureTrafficOnly': False} + 'secureTrafficOnly': False, 'intermediateCertificate': ''} - self.m.StubOutWithMock(fake_loadbalancer, 'add_ssl_termination') - fake_loadbalancer.add_ssl_termination( + self.m.StubOutWithMock(fake_lb, 'get_ssl_termination') + fake_lb.get_ssl_termination().AndReturn({}) + fake_lb.get_ssl_termination().AndReturn({ + 'securePort': 443, 'privatekey': private_key, 'certificate': cert, + 'secureTrafficOnly': False, 'intermediateCertificate': ''}) + + self.m.StubOutWithMock(fake_lb, 'add_ssl_termination') + fake_lb.add_ssl_termination( securePort=443, privatekey=private_key, certificate=cert, - secureTrafficOnly=False, intermediateCertificate=None) + secureTrafficOnly=False, intermediateCertificate='') self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -1138,9 +1310,9 @@ class LoadBalancerTest(common.HeatTestCase): 'secureTrafficOnly': False} # The SSL termination config is done post-creation, so no need # to modify self.expected_body - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1148,8 +1320,15 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['sslTermination'] - self.m.StubOutWithMock(fake_loadbalancer, 'delete_ssl_termination') - fake_loadbalancer.delete_ssl_termination() + self.m.StubOutWithMock(fake_lb, 'get_ssl_termination') + fake_lb.get_ssl_termination().AndReturn({ + 'securePort': 443, 'privatekey': private_key, 'certificate': cert, + 'secureTrafficOnly': False}) + + self.m.StubOutWithMock(fake_lb, 'delete_ssl_termination') + fake_lb.delete_ssl_termination() + + fake_lb.get_ssl_termination().AndReturn({}) self.m.ReplayAll() @@ -1158,17 +1337,21 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_metadata_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['metadata'] = {'a': 1, 'b': 2} - self.m.StubOutWithMock(fake_loadbalancer, 'set_metadata') - fake_loadbalancer.set_metadata({'a': 1, 'b': 2}) + self.m.StubOutWithMock(fake_lb, 'get_metadata') + fake_lb.get_metadata().AndReturn({}) + fake_lb.get_metadata().AndReturn({'a': 1, 'b': 2}) + + self.m.StubOutWithMock(fake_lb, 'set_metadata') + fake_lb.set_metadata({'a': 1, 'b': 2}) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -1184,7 +1367,7 @@ class LoadBalancerTest(common.HeatTestCase): expected_body['metadata'] = mox.SameElementsAs( [{'key': 'a', 'value': 1}, {'key': 'b', 'value': 2}]) - rsrc, fake_loadbalancer = self._mock_loadbalancer( + rsrc, fake_lb = self._mock_loadbalancer( template, self.lb_name, expected_body) self.m.ReplayAll() @@ -1193,8 +1376,12 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['metadata'] - self.m.StubOutWithMock(fake_loadbalancer, 'delete_metadata') - fake_loadbalancer.delete_metadata() + self.m.StubOutWithMock(fake_lb, 'get_metadata') + fake_lb.get_metadata().AndReturn({'a': 1, 'b': 2}) + fake_lb.get_metadata().AndReturn({}) + + self.m.StubOutWithMock(fake_lb, 'delete_metadata') + fake_lb.delete_metadata() self.m.ReplayAll() @@ -1203,9 +1390,9 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_errorpage_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1216,8 +1403,14 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) update_template['Properties']['errorPage'] = error_page - self.m.StubOutWithMock(fake_loadbalancer, 'set_error_page') - fake_loadbalancer.set_error_page(error_page) + self.m.StubOutWithMock(fake_lb, 'get_error_page') + fake_lb.get_error_page().AndReturn( + {'errorpage': {'content': 'foo'}}) + fake_lb.get_error_page().AndReturn( + {'errorpage': {'content': error_page}}) + + self.m.StubOutWithMock(fake_lb, 'set_error_page') + fake_lb.set_error_page(error_page) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -1233,9 +1426,9 @@ class LoadBalancerTest(common.HeatTestCase): template['Resources'][lb_name]['Properties']['errorPage'] = error_page # The error page config is done post-creation, so no need to # modify self.expected_body - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1243,8 +1436,13 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['errorPage'] - self.m.StubOutWithMock(fake_loadbalancer, 'clear_error_page') - fake_loadbalancer.clear_error_page() + self.m.StubOutWithMock(fake_lb, 'clear_error_page') + fake_lb.clear_error_page() + + self.m.StubOutWithMock(fake_lb, 'get_error_page') + fake_lb.get_error_page().AndReturn( + {'errorpage': {'content': error_page}}) + fake_lb.get_error_page().AndReturn({'errorpage': {'content': ""}}) self.m.ReplayAll() @@ -1253,9 +1451,9 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_connection_logging_enable(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1265,7 +1463,7 @@ class LoadBalancerTest(common.HeatTestCase): self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(True, fake_loadbalancer.connection_logging) + self.assertEqual(True, fake_lb.connection_logging) self.m.VerifyAll() def test_update_connection_logging_delete(self): @@ -1275,13 +1473,23 @@ class LoadBalancerTest(common.HeatTestCase): 'connectionLogging'] = True expected_body = copy.deepcopy(self.expected_body) expected_body['connectionLogging'] = {'enabled': True} - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.connection_logging = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.connection_logging = False + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) + update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['connectionLogging'] @@ -1289,7 +1497,7 @@ class LoadBalancerTest(common.HeatTestCase): scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(False, fake_loadbalancer.connection_logging) + self.assertEqual(False, fake_lb.connection_logging) self.m.VerifyAll() def test_update_connection_logging_disable(self): @@ -1299,9 +1507,9 @@ class LoadBalancerTest(common.HeatTestCase): 'connectionLogging'] = True expected_body = copy.deepcopy(self.expected_body) expected_body['connectionLogging'] = {'enabled': True} - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1313,13 +1521,13 @@ class LoadBalancerTest(common.HeatTestCase): scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(False, fake_loadbalancer.connection_logging) + self.assertEqual(False, fake_lb.connection_logging) self.m.VerifyAll() def test_update_connection_throttle_add(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1327,10 +1535,20 @@ class LoadBalancerTest(common.HeatTestCase): update_template['Properties']['connectionThrottle'] = { 'maxConnections': 1000} - self.m.StubOutWithMock(fake_loadbalancer, 'add_connection_throttle') - fake_loadbalancer.add_connection_throttle( + self.m.StubOutWithMock(fake_lb, 'add_connection_throttle') + self.m.StubOutWithMock(fake_lb, 'get_connection_throttle') + fake_lb.get_connection_throttle().AndReturn( + {'maxConnectionRate': None, 'minConnections': None, + 'rateInterval': None, 'maxConnections': 100}) + + fake_lb.add_connection_throttle( maxConnections=1000, maxConnectionRate=None, minConnections=None, rateInterval=None) + + fake_lb.get_connection_throttle().AndReturn( + {'maxConnectionRate': None, 'minConnections': None, + 'rateInterval': None, 'maxConnections': 1000}) + self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) @@ -1345,9 +1563,9 @@ class LoadBalancerTest(common.HeatTestCase): expected_body['connectionThrottle'] = { 'maxConnections': 1000, 'maxConnectionRate': None, 'rateInterval': None, 'minConnections': None} - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1355,9 +1573,15 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['connectionThrottle'] - self.m.StubOutWithMock(fake_loadbalancer, 'delete_connection_throttle') - fake_loadbalancer.delete_connection_throttle() + self.m.StubOutWithMock(fake_lb, 'get_connection_throttle') + fake_lb.get_connection_throttle().AndReturn({ + 'maxConnections': 1000, 'maxConnectionRate': None, + 'rateInterval': None, 'minConnections': None}) + self.m.StubOutWithMock(fake_lb, 'delete_connection_throttle') + fake_lb.delete_connection_throttle() + + fake_lb.get_connection_throttle().AndReturn({}) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() @@ -1365,20 +1589,27 @@ class LoadBalancerTest(common.HeatTestCase): self.m.VerifyAll() def test_update_content_caching_enable(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) update_template['Properties']['contentCaching'] = 'ENABLED' + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.content_caching = False + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.content_caching = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(True, fake_loadbalancer.content_caching) self.m.VerifyAll() def test_update_content_caching_deleted(self): @@ -1388,22 +1619,27 @@ class LoadBalancerTest(common.HeatTestCase): 'contentCaching'] = 'ENABLED' # Enabling the content cache is done post-creation, so no need # to modify self.expected_body - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) - + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() update_template = copy.deepcopy(rsrc.t) del update_template['Properties']['contentCaching'] + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.content_caching = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.content_caching = False + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) self.m.ReplayAll() - self.assertEqual(True, fake_loadbalancer.content_caching) scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(False, fake_loadbalancer.content_caching) self.m.VerifyAll() def test_update_content_caching_disable(self): @@ -1413,9 +1649,9 @@ class LoadBalancerTest(common.HeatTestCase): 'contentCaching'] = 'ENABLED' # Enabling the content cache is done post-creation, so no need # to modify self.expected_body - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - self.expected_body) + rsrc, fake_lb = self._mock_loadbalancer(template, + self.lb_name, + self.expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1423,12 +1659,18 @@ class LoadBalancerTest(common.HeatTestCase): update_template = copy.deepcopy(rsrc.t) update_template['Properties']['contentCaching'] = 'DISABLED' + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + fake_lb1.content_caching = True + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.content_caching = False + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) self.m.ReplayAll() - self.assertEqual(True, fake_loadbalancer.content_caching) scheduler.TaskRunner(rsrc.update, update_template)() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) - self.assertEqual(False, fake_loadbalancer.content_caching) self.m.VerifyAll() def test_delete(self): @@ -1604,10 +1846,10 @@ class LoadBalancerTest(common.HeatTestCase): self.assertIn("HTTPS redirect is only available", six.text_type(ex)) def test_update_nodes_condition_draining(self): - rsrc, fake_loadbalancer = self._mock_loadbalancer(self.lb_template, - self.lb_name, - self.expected_body) - fake_loadbalancer.nodes = self.expected_body['nodes'] + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) + fake_lb.nodes = self.expected_body['nodes'] self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() @@ -1621,11 +1863,70 @@ class LoadBalancerTest(common.HeatTestCase): "port": 80, "condition": "DRAINING"}] - self.m.StubOutWithMock(fake_loadbalancer, 'add_nodes') - fake_loadbalancer.add_nodes([ - fake_loadbalancer.Node(address=expected_ip, - port=80, - condition='DRAINING')]) + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb1, 'add_nodes') + fake_lb1.add_nodes([ + fake_lb1.Node(address=expected_ip, + port=80, + condition='DRAINING')]) + + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.nodes = [ + FakeNode(address=u"166.78.103.141", port=80, + condition=u"DRAINING"), + FakeNode(address=u"172.168.1.4", port=80, + condition=u"DRAINING"), + ] + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) + + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.update, update_template)() + self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + def test_update_nodes_add_same_address_different_port(self): + rsrc, fake_lb = self._mock_loadbalancer(self.lb_template, + self.lb_name, + self.expected_body) + fake_lb.nodes = self.expected_body['nodes'] + fake_lb.tracker = "fake_lb" + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['nodes'] = [ + {"addresses": ["166.78.103.141"], + "port": 80, + "condition": "ENABLED"}, + {"addresses": ["166.78.103.141"], + "port": 81, + "condition": "ENABLED"}] + + self.m.UnsetStubs() + self.m.StubOutWithMock(rsrc.clb, 'get') + fake_lb1 = copy.deepcopy(fake_lb) + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb1) + + self.m.StubOutWithMock(fake_lb1, 'add_nodes') + fake_lb1.add_nodes([ + fake_lb1.Node(address="166.78.103.141", + port=81, + condition='ENABLED')]) + fake_lb1.tracker = "fake_lb1" + + fake_lb2 = copy.deepcopy(fake_lb) + fake_lb2.nodes = [ + FakeNode(address=u"166.78.103.141", port=80, + condition=u"ENABLED"), + FakeNode(address=u"166.78.103.141", port=81, + condition=u"ENABLED"), + ] + fake_lb2.tracker = "fake_lb2" + rsrc.clb.get(mox.IgnoreArg()).AndReturn(fake_lb2) self.m.ReplayAll() scheduler.TaskRunner(rsrc.update, update_template)()