From 80534e1233e7f26e78e7c0312ac9e60e553c6fbf Mon Sep 17 00:00:00 2001 From: Nick Bailey Date: Mon, 31 Dec 2012 20:24:53 +0000 Subject: [PATCH] Allow passing multiple nodes to wait_until_running Additionally, makes this method public. --- libcloud/compute/base.py | 37 +++++++++-------- ...ug_servers_detail_deployment_multiple_nodes.xml | 26 ++++++++++++ libcloud/test/compute/test_deployment.py | 42 ++++++++++++++------ 3 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 libcloud/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_multiple_nodes.xml diff --git a/libcloud/compute/base.py b/libcloud/compute/base.py index 09aac36..530c871 100644 --- a/libcloud/compute/base.py +++ b/libcloud/compute/base.py @@ -650,10 +650,10 @@ class NodeDriver(BaseDriver): try: # Wait until node is up and running and has IP assigned ssh_interface = kwargs.get('ssh_interface', 'public_ips') - node, ip_addresses = self._wait_until_running( - node=node, + node, ip_addresses = self.wait_until_running( + nodes=[node], wait_period=3, timeout=NODE_ONLINE_WAIT_TIMEOUT, - ssh_interface=ssh_interface) + ssh_interface=ssh_interface)[0] if password: node.extra['password'] = password @@ -753,13 +753,13 @@ class NodeDriver(BaseDriver): raise NotImplementedError('detach not implemented for this driver') - def _wait_until_running(self, node, wait_period=3, timeout=600, - ssh_interface='public_ips', force_ipv4=True): + def wait_until_running(self, nodes, wait_period=3, timeout=600, + ssh_interface='public_ips', force_ipv4=True): """ - Block until node is fully booted and has an IP address assigned. + Block until the given nodes are fully booted and have an IP address assigned. - @keyword node: Node instance. - @type node: C{Node} + @keyword nodes: list of node instances. + @type nodes: C{[Nodes]} @keyword wait_period: How many seconds to between each loop iteration (default is 3) @@ -777,7 +777,7 @@ class NodeDriver(BaseDriver): @keyword force_ipv4: Ignore ipv6 IP addresses (default is True). @type force_ipv4: C{bool} - @return: C{(Node, ip_addresses)} tuple of Node instance and + @return: C{[(Node, ip_addresses)]} list of tuple of Node instance and list of ip_address on success. """ def is_supported(address): @@ -798,19 +798,22 @@ class NodeDriver(BaseDriver): raise ValueError('ssh_interface argument must either be' + 'public_ips or private_ips') + uuids = set([n.uuid for n in nodes]) while time.time() < end: nodes = self.list_nodes() - nodes = list([n for n in nodes if n.uuid == node.uuid]) + nodes = list([n for n in nodes if n.uuid in uuids]) - if len(nodes) > 1: - raise LibcloudError(value=('Booted single node[%s], ' % node - + 'but multiple nodes have same UUID'), + if len(nodes) > len(uuids): + found_uuids = [n.uuid for n in nodes] + raise LibcloudError(value=('Unable to match specified uuids ' + + '(%s) with existing nodes. Found ' % uuids + + 'multiple nodes with same uuid: (%s)' % found_uuids), driver=self) - if (len(nodes) == 1 and nodes[0].state == NodeState.RUNNING and - filter_addresses(getattr(nodes[0], ssh_interface))): - return (nodes[0], filter_addresses(getattr(nodes[0], - ssh_interface))) + running_nodes = [n for n in nodes if n.state == NodeState.RUNNING] + addresses = [filter_addresses(getattr(n, ssh_interface)) for n in running_nodes] + if len(running_nodes) == len(uuids) == len(addresses): + return zip(running_nodes, addresses) else: time.sleep(wait_period) continue diff --git a/libcloud/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_multiple_nodes.xml b/libcloud/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_multiple_nodes.xml new file mode 100644 index 0000000..44babd8 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack/v1_slug_servers_detail_deployment_multiple_nodes.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libcloud/test/compute/test_deployment.py b/libcloud/test/compute/test_deployment.py index eb6f992..bf1d0c6 100644 --- a/libcloud/test/compute/test_deployment.py +++ b/libcloud/test/compute/test_deployment.py @@ -70,6 +70,9 @@ class DeploymentTests(unittest.TestCase): self.node = Node(id=12345, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) + self.node2 = Node(id=123456, name='test', state=NodeState.RUNNING, + public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], + driver=Rackspace) def test_multi_step_deployment(self): msd = MultiStepDeployment() @@ -139,28 +142,28 @@ class DeploymentTests(unittest.TestCase): self.fail('TypeError was not thrown') def test_wait_until_running_running_instantly(self): - node2, ips = self.driver._wait_until_running(node=self.node, wait_period=1, - timeout=10) + node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, + timeout=10)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_1_second(self): RackspaceMockHttp.type = '1_SECOND_DELAY' - node2, ips = self.driver._wait_until_running(node=self.node, wait_period=1, - timeout=10) + node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, + timeout=10)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_1_second_private_ips(self): RackspaceMockHttp.type = '1_SECOND_DELAY' - node2, ips = self.driver._wait_until_running(node=self.node, wait_period=1, - timeout=10, ssh_interface='private_ips') + node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, + timeout=10, ssh_interface='private_ips')[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['10.176.168.218'], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: - self.driver._wait_until_running(node=self.node, wait_period=1, + self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface='invalid') except ValueError: pass @@ -171,7 +174,7 @@ class DeploymentTests(unittest.TestCase): RackspaceMockHttp.type = 'TIMEOUT' try: - self.driver._wait_until_running(node=self.node, wait_period=0.5, + self.driver.wait_until_running(nodes=[self.node], wait_period=0.5, timeout=1) except LibcloudError: e = sys.exc_info()[1] @@ -183,8 +186,8 @@ class DeploymentTests(unittest.TestCase): RackspaceMockHttp.type = 'MISSING' try: - self.driver._wait_until_running(node=self.node, wait_period=0.5, - timeout=1) + self.driver.wait_until_running(nodes=[self.node], wait_period=0.5, + timeout=1) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Timed out after 1 second') != -1) @@ -195,14 +198,25 @@ class DeploymentTests(unittest.TestCase): RackspaceMockHttp.type = 'SAME_UUID' try: - self.driver._wait_until_running(node=self.node, wait_period=0.5, + self.driver.wait_until_running(nodes=[self.node], wait_period=0.5, timeout=1) except LibcloudError: e = sys.exc_info()[1] - self.assertTrue(e.value.find('multiple nodes have same UUID') != -1) + self.assertTrue(e.value.find('Unable to match specified uuids') != -1) else: self.fail('Exception was not thrown') + def test_wait_until_running_running_wait_for_multiple_nodes(self): + RackspaceMockHttp.type = 'MULTIPLE_NODES' + + nodes = self.driver.wait_until_running(nodes=[self.node, self.node2], wait_period=0.5, + timeout=1) + self.assertEqual(self.node.uuid, nodes[0][0].uuid) + self.assertEqual(self.node2.uuid, nodes[1][0].uuid) + self.assertEqual(['67.23.21.33'], nodes[0][1]) + self.assertEqual(['67.23.21.34'], nodes[1][1]) + + def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None @@ -400,6 +414,10 @@ class RackspaceMockHttp(MockHttp): body = self.fixtures.load('v1_slug_servers_detail_deployment_same_uuid.xml') return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) + def _v1_0_slug_servers_detail_MULTIPLE_NODES(self, method, url, body, headers): + body = self.fixtures.load('v1_slug_servers_detail_deployment_multiple_nodes.xml') + return (httplib.OK, body, XML_HEADERS, httplib.responses[httplib.OK]) + if __name__ == '__main__': sys.exit(unittest.main()) -- 1.7.9.5