blob: aa0f082626736aaf9574794b88230c87591b433e [file] [log] [blame]
James E. Blair15d91cc2017-01-18 09:05:17 -08001# Copyright 2017 Red Hat, Inc.
James E. Blair3897a132016-12-22 18:23:42 -08002#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15
16import time
17
18import zuul.zk
19import zuul.nodepool
20from zuul import model
21
22from tests.base import BaseTestCase, ChrootedKazooFixture, FakeNodepool
23
24
25class TestNodepool(BaseTestCase):
26 # Tests the Nodepool interface class using a fake nodepool and
27 # scheduler.
28
29 def setUp(self):
Clark Boylan24620182017-04-24 10:11:51 -070030 super(TestNodepool, self).setUp()
James E. Blair3897a132016-12-22 18:23:42 -080031
James E. Blair4f1731b2017-10-10 18:11:42 -070032 self.statsd = None
Clark Boylan621ec9a2017-04-07 17:41:33 -070033 self.zk_chroot_fixture = self.useFixture(
34 ChrootedKazooFixture(self.id()))
James E. Blair0d5a36e2017-02-21 10:53:44 -050035 self.zk_config = '%s:%s%s' % (
James E. Blair3897a132016-12-22 18:23:42 -080036 self.zk_chroot_fixture.zookeeper_host,
37 self.zk_chroot_fixture.zookeeper_port,
38 self.zk_chroot_fixture.zookeeper_chroot)
39
40 self.zk = zuul.zk.ZooKeeper()
Clark Boylanffe8f8b2017-04-24 17:36:11 -070041 self.addCleanup(self.zk.disconnect)
James E. Blair0d5a36e2017-02-21 10:53:44 -050042 self.zk.connect(self.zk_config)
James E. Blair8b2a1472017-02-19 15:33:55 -080043 self.hostname = 'nodepool-test-hostname'
James E. Blair3897a132016-12-22 18:23:42 -080044
45 self.provisioned_requests = []
46 # This class implements the scheduler methods zuul.nodepool
47 # needs, so we pass 'self' as the scheduler.
48 self.nodepool = zuul.nodepool.Nodepool(self)
49
James E. Blair0d5a36e2017-02-21 10:53:44 -050050 self.fake_nodepool = FakeNodepool(
51 self.zk_chroot_fixture.zookeeper_host,
52 self.zk_chroot_fixture.zookeeper_port,
53 self.zk_chroot_fixture.zookeeper_chroot)
Clark Boylanffe8f8b2017-04-24 17:36:11 -070054 self.addCleanup(self.fake_nodepool.stop)
James E. Blair3897a132016-12-22 18:23:42 -080055
56 def waitForRequests(self):
57 # Wait until all requests are complete.
58 while self.nodepool.requests:
59 time.sleep(0.1)
60
61 def onNodesProvisioned(self, request):
62 # This is a scheduler method that the nodepool class calls
63 # back when a request is provisioned.
64 self.provisioned_requests.append(request)
65
66 def test_node_request(self):
67 # Test a simple node request
68
69 nodeset = model.NodeSet()
Paul Belangerecb0b842017-11-18 15:23:29 -050070 nodeset.addNode(model.Node(['controller', 'foo'], 'ubuntu-xenial'))
71 nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial'))
James E. Blair3897a132016-12-22 18:23:42 -080072 job = model.Job('testjob')
73 job.nodeset = nodeset
74 request = self.nodepool.requestNodes(None, job)
75 self.waitForRequests()
76 self.assertEqual(len(self.provisioned_requests), 1)
77 self.assertEqual(request.state, 'fulfilled')
James E. Blair15be0e12017-01-03 13:45:20 -080078
James E. Blaira38c28e2017-01-04 10:33:20 -080079 # Accept the nodes
David Shrewsbury94e95882017-10-04 15:26:04 -040080 self.nodepool.acceptNodes(request, request.id)
James E. Blaira38c28e2017-01-04 10:33:20 -080081 nodeset = request.nodeset
82
83 for node in nodeset.getNodes():
84 self.assertIsNotNone(node.lock)
85 self.assertEqual(node.state, 'ready')
86
James E. Blaircacdf2b2017-01-04 13:14:37 -080087 # Mark the nodes in use
James E. Blair1511bc32017-01-18 09:25:31 -080088 self.nodepool.useNodeSet(nodeset)
James E. Blaircacdf2b2017-01-04 13:14:37 -080089 for node in nodeset.getNodes():
90 self.assertEqual(node.state, 'in-use')
91
James E. Blair62295d32017-01-04 13:27:58 -080092 # Return the nodes
James E. Blair1511bc32017-01-18 09:25:31 -080093 self.nodepool.returnNodeSet(nodeset)
James E. Blair62295d32017-01-04 13:27:58 -080094 for node in nodeset.getNodes():
95 self.assertIsNone(node.lock)
96 self.assertEqual(node.state, 'used')
97
James E. Blair15be0e12017-01-03 13:45:20 -080098 def test_node_request_disconnect(self):
99 # Test that node requests are re-submitted after disconnect
100
101 nodeset = model.NodeSet()
Paul Belangerecb0b842017-11-18 15:23:29 -0500102 nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial'))
103 nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial'))
James E. Blair15be0e12017-01-03 13:45:20 -0800104 job = model.Job('testjob')
105 job.nodeset = nodeset
106 self.fake_nodepool.paused = True
107 request = self.nodepool.requestNodes(None, job)
108 self.zk.client.stop()
109 self.zk.client.start()
110 self.fake_nodepool.paused = False
111 self.waitForRequests()
112 self.assertEqual(len(self.provisioned_requests), 1)
113 self.assertEqual(request.state, 'fulfilled')
James E. Blair01695c32017-01-04 17:29:25 -0800114
115 def test_node_request_canceled(self):
116 # Test that node requests can be canceled
117
118 nodeset = model.NodeSet()
Paul Belangerecb0b842017-11-18 15:23:29 -0500119 nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial'))
120 nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial'))
James E. Blair01695c32017-01-04 17:29:25 -0800121 job = model.Job('testjob')
122 job.nodeset = nodeset
123 self.fake_nodepool.paused = True
124 request = self.nodepool.requestNodes(None, job)
125 self.nodepool.cancelRequest(request)
126
127 self.waitForRequests()
128 self.assertEqual(len(self.provisioned_requests), 0)
David Shrewsbury94e95882017-10-04 15:26:04 -0400129
130 def test_accept_nodes_resubmitted(self):
131 # Test that a resubmitted request would not lock nodes
132
133 nodeset = model.NodeSet()
Paul Belangerecb0b842017-11-18 15:23:29 -0500134 nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial'))
135 nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial'))
David Shrewsbury94e95882017-10-04 15:26:04 -0400136 job = model.Job('testjob')
137 job.nodeset = nodeset
138 request = self.nodepool.requestNodes(None, job)
139 self.waitForRequests()
140 self.assertEqual(len(self.provisioned_requests), 1)
141 self.assertEqual(request.state, 'fulfilled')
142
143 # Accept the nodes, passing a different ID
144 self.nodepool.acceptNodes(request, "invalid")
145 nodeset = request.nodeset
146
147 for node in nodeset.getNodes():
148 self.assertIsNone(node.lock)
149 self.assertEqual(node.state, 'ready')
150
151 def test_accept_nodes_lost_request(self):
152 # Test that a lost request would not lock nodes
153
154 nodeset = model.NodeSet()
Paul Belangerecb0b842017-11-18 15:23:29 -0500155 nodeset.addNode(model.Node(['controller'], 'ubuntu-xenial'))
156 nodeset.addNode(model.Node(['compute'], 'ubuntu-xenial'))
David Shrewsbury94e95882017-10-04 15:26:04 -0400157 job = model.Job('testjob')
158 job.nodeset = nodeset
159 request = self.nodepool.requestNodes(None, job)
160 self.waitForRequests()
161 self.assertEqual(len(self.provisioned_requests), 1)
162 self.assertEqual(request.state, 'fulfilled')
163
164 self.zk.deleteNodeRequest(request)
165
166 # Accept the nodes
167 self.nodepool.acceptNodes(request, request.id)
168 nodeset = request.nodeset
169
170 for node in nodeset.getNodes():
171 self.assertIsNone(node.lock)
172 self.assertEqual(node.state, 'ready')