1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5#      http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import random
14import uuid
15
16from openstack import exceptions
17from openstack.tests.functional.baremetal import base
18
19
20class TestBareMetalNode(base.BaseBaremetalTest):
21    def test_node_create_get_delete(self):
22        node = self.create_node(name='node-name')
23        self.assertEqual(node.name, 'node-name')
24        self.assertEqual(node.driver, 'fake-hardware')
25        self.assertEqual(node.provision_state, 'available')
26        self.assertFalse(node.is_maintenance)
27
28        # NOTE(dtantsur): get_node and find_node only differ in handing missing
29        # nodes, otherwise they are identical.
30        for call, ident in [(self.conn.baremetal.get_node, self.node_id),
31                            (self.conn.baremetal.get_node, 'node-name'),
32                            (self.conn.baremetal.find_node, self.node_id),
33                            (self.conn.baremetal.find_node, 'node-name')]:
34            found = call(ident)
35            self.assertEqual(node.id, found.id)
36            self.assertEqual(node.name, found.name)
37
38        with_fields = self.conn.baremetal.get_node(
39            'node-name',
40            fields=['uuid', 'driver', 'instance_id'])
41        self.assertEqual(node.id, with_fields.id)
42        self.assertEqual(node.driver, with_fields.driver)
43        self.assertIsNone(with_fields.name)
44        self.assertIsNone(with_fields.provision_state)
45
46        nodes = self.conn.baremetal.nodes()
47        self.assertIn(node.id, [n.id for n in nodes])
48
49        self.conn.baremetal.delete_node(node, ignore_missing=False)
50        self.assertRaises(exceptions.ResourceNotFound,
51                          self.conn.baremetal.get_node, self.node_id)
52
53    def test_node_update(self):
54        node = self.create_node(name='node-name', extra={'foo': 'bar'})
55        node.name = 'new-name'
56        node.extra = {'answer': 42}
57        instance_uuid = str(uuid.uuid4())
58
59        node = self.conn.baremetal.update_node(node,
60                                               instance_id=instance_uuid)
61        self.assertEqual('new-name', node.name)
62        self.assertEqual({'answer': 42}, node.extra)
63        self.assertEqual(instance_uuid, node.instance_id)
64
65        node = self.conn.baremetal.get_node('new-name')
66        self.assertEqual('new-name', node.name)
67        self.assertEqual({'answer': 42}, node.extra)
68        self.assertEqual(instance_uuid, node.instance_id)
69
70        node = self.conn.baremetal.update_node(node,
71                                               instance_id=None)
72        self.assertIsNone(node.instance_id)
73
74        node = self.conn.baremetal.get_node('new-name')
75        self.assertIsNone(node.instance_id)
76
77    def test_node_update_by_name(self):
78        self.create_node(name='node-name', extra={'foo': 'bar'})
79        instance_uuid = str(uuid.uuid4())
80
81        node = self.conn.baremetal.update_node('node-name',
82                                               instance_id=instance_uuid,
83                                               extra={'answer': 42})
84        self.assertEqual({'answer': 42}, node.extra)
85        self.assertEqual(instance_uuid, node.instance_id)
86
87        node = self.conn.baremetal.get_node('node-name')
88        self.assertEqual({'answer': 42}, node.extra)
89        self.assertEqual(instance_uuid, node.instance_id)
90
91        node = self.conn.baremetal.update_node('node-name',
92                                               instance_id=None)
93        self.assertIsNone(node.instance_id)
94
95        node = self.conn.baremetal.get_node('node-name')
96        self.assertIsNone(node.instance_id)
97
98    def test_node_patch(self):
99        node = self.create_node(name='node-name', extra={'foo': 'bar'})
100        node.name = 'new-name'
101        instance_uuid = str(uuid.uuid4())
102
103        node = self.conn.baremetal.patch_node(
104            node,
105            [dict(path='/instance_id', op='replace', value=instance_uuid),
106             dict(path='/extra/answer', op='add', value=42)])
107        self.assertEqual('new-name', node.name)
108        self.assertEqual({'foo': 'bar', 'answer': 42}, node.extra)
109        self.assertEqual(instance_uuid, node.instance_id)
110
111        node = self.conn.baremetal.get_node('new-name')
112        self.assertEqual('new-name', node.name)
113        self.assertEqual({'foo': 'bar', 'answer': 42}, node.extra)
114        self.assertEqual(instance_uuid, node.instance_id)
115
116        node = self.conn.baremetal.patch_node(
117            node,
118            [dict(path='/instance_id', op='remove'),
119             dict(path='/extra/answer', op='remove')])
120        self.assertIsNone(node.instance_id)
121        self.assertNotIn('answer', node.extra)
122
123        node = self.conn.baremetal.get_node('new-name')
124        self.assertIsNone(node.instance_id)
125        self.assertNotIn('answer', node.extra)
126
127    def test_node_list_update_delete(self):
128        self.create_node(name='node-name', extra={'foo': 'bar'})
129        node = next(n for n in
130                    self.conn.baremetal.nodes(details=True,
131                                              provision_state='available',
132                                              is_maintenance=False,
133                                              associated=False)
134                    if n.name == 'node-name')
135        self.assertEqual(node.extra, {'foo': 'bar'})
136
137        # This test checks that resources returned from listing are usable
138        self.conn.baremetal.update_node(node, extra={'foo': 42})
139        self.conn.baremetal.delete_node(node, ignore_missing=False)
140
141    def test_node_create_in_enroll_provide(self):
142        node = self.create_node(provision_state='enroll')
143        self.node_id = node.id
144
145        self.assertEqual(node.driver, 'fake-hardware')
146        self.assertEqual(node.provision_state, 'enroll')
147        self.assertIsNone(node.power_state)
148        self.assertFalse(node.is_maintenance)
149
150        self.conn.baremetal.set_node_provision_state(node, 'manage',
151                                                     wait=True)
152        self.assertEqual(node.provision_state, 'manageable')
153
154        self.conn.baremetal.set_node_provision_state(node, 'provide',
155                                                     wait=True)
156        self.assertEqual(node.provision_state, 'available')
157
158    def test_node_create_in_enroll_provide_by_name(self):
159        name = 'node-%d' % random.randint(0, 1000)
160        node = self.create_node(provision_state='enroll', name=name)
161        self.node_id = node.id
162
163        self.assertEqual(node.driver, 'fake-hardware')
164        self.assertEqual(node.provision_state, 'enroll')
165        self.assertIsNone(node.power_state)
166        self.assertFalse(node.is_maintenance)
167
168        node = self.conn.baremetal.set_node_provision_state(name, 'manage',
169                                                            wait=True)
170        self.assertEqual(node.provision_state, 'manageable')
171
172        node = self.conn.baremetal.set_node_provision_state(name, 'provide',
173                                                            wait=True)
174        self.assertEqual(node.provision_state, 'available')
175
176    def test_node_power_state(self):
177        node = self.create_node()
178        self.assertIsNone(node.power_state)
179
180        self.conn.baremetal.set_node_power_state(node, 'power on', wait=True)
181        node = self.conn.baremetal.get_node(node.id)
182        self.assertEqual('power on', node.power_state)
183
184        self.conn.baremetal.set_node_power_state(node, 'power off', wait=True)
185        node = self.conn.baremetal.get_node(node.id)
186        self.assertEqual('power off', node.power_state)
187
188    def test_node_validate(self):
189        node = self.create_node()
190        # Fake hardware passes validation for all interfaces
191        result = self.conn.baremetal.validate_node(node)
192        for iface in ('boot', 'deploy', 'management', 'power'):
193            self.assertTrue(result[iface].result)
194            self.assertFalse(result[iface].reason)
195
196    def test_node_negative_non_existing(self):
197        uuid = "5c9dcd04-2073-49bc-9618-99ae634d8971"
198        self.assertRaises(exceptions.ResourceNotFound,
199                          self.conn.baremetal.get_node, uuid)
200        self.assertRaises(exceptions.ResourceNotFound,
201                          self.conn.baremetal.find_node, uuid,
202                          ignore_missing=False)
203        self.assertRaises(exceptions.ResourceNotFound,
204                          self.conn.baremetal.delete_node, uuid,
205                          ignore_missing=False)
206        self.assertRaises(exceptions.ResourceNotFound,
207                          self.conn.baremetal.update_node, uuid,
208                          name='new-name')
209        self.assertIsNone(self.conn.baremetal.find_node(uuid))
210        self.assertIsNone(self.conn.baremetal.delete_node(uuid))
211
212    def test_maintenance(self):
213        reason = "Prepating for taking over the world"
214
215        node = self.create_node()
216        self.assertFalse(node.is_maintenance)
217        self.assertIsNone(node.maintenance_reason)
218
219        # Initial setting without the reason
220        node = self.conn.baremetal.set_node_maintenance(node)
221        self.assertTrue(node.is_maintenance)
222        self.assertIsNone(node.maintenance_reason)
223
224        # Updating the reason later
225        node = self.conn.baremetal.set_node_maintenance(node, reason)
226        self.assertTrue(node.is_maintenance)
227        self.assertEqual(reason, node.maintenance_reason)
228
229        # Removing the reason later
230        node = self.conn.baremetal.set_node_maintenance(node)
231        self.assertTrue(node.is_maintenance)
232        self.assertIsNone(node.maintenance_reason)
233
234        # Unsetting maintenance
235        node = self.conn.baremetal.unset_node_maintenance(node)
236        self.assertFalse(node.is_maintenance)
237        self.assertIsNone(node.maintenance_reason)
238
239        # Initial setting with the reason
240        node = self.conn.baremetal.set_node_maintenance(node, reason)
241        self.assertTrue(node.is_maintenance)
242        self.assertEqual(reason, node.maintenance_reason)
243
244    def test_maintenance_via_update(self):
245        reason = "Prepating for taking over the world"
246
247        node = self.create_node()
248
249        # Initial setting without the reason
250        node = self.conn.baremetal.update_node(node, is_maintenance=True)
251        self.assertTrue(node.is_maintenance)
252        self.assertIsNone(node.maintenance_reason)
253
254        # Make sure the change has effect on the remote side.
255        node = self.conn.baremetal.get_node(node.id)
256        self.assertTrue(node.is_maintenance)
257        self.assertIsNone(node.maintenance_reason)
258
259        # Updating the reason later
260        node = self.conn.baremetal.update_node(node, maintenance_reason=reason)
261        self.assertTrue(node.is_maintenance)
262        self.assertEqual(reason, node.maintenance_reason)
263
264        # Make sure the change has effect on the remote side.
265        node = self.conn.baremetal.get_node(node.id)
266        self.assertTrue(node.is_maintenance)
267        self.assertEqual(reason, node.maintenance_reason)
268
269        # Unsetting maintenance
270        node = self.conn.baremetal.update_node(node, is_maintenance=False)
271        self.assertFalse(node.is_maintenance)
272        self.assertIsNone(node.maintenance_reason)
273
274        # Make sure the change has effect on the remote side.
275        node = self.conn.baremetal.get_node(node.id)
276        self.assertFalse(node.is_maintenance)
277        self.assertIsNone(node.maintenance_reason)
278
279        # Initial setting with the reason
280        node = self.conn.baremetal.update_node(node, is_maintenance=True,
281                                               maintenance_reason=reason)
282        self.assertTrue(node.is_maintenance)
283        self.assertEqual(reason, node.maintenance_reason)
284
285        # Make sure the change has effect on the remote side.
286        node = self.conn.baremetal.get_node(node.id)
287        self.assertTrue(node.is_maintenance)
288        self.assertEqual(reason, node.maintenance_reason)
289
290
291class TestNodeRetired(base.BaseBaremetalTest):
292
293    min_microversion = '1.61'
294
295    def test_retired(self):
296        reason = "I'm too old for this s...tuff!"
297
298        node = self.create_node()
299
300        # Set retired when node state available should fail!
301        self.assertRaises(
302            exceptions.ConflictException,
303            self.conn.baremetal.update_node, node, is_retired=True)
304
305        # Set node state to manageable
306        self.conn.baremetal.set_node_provision_state(node, 'manage',
307                                                     wait=True)
308        self.assertEqual(node.provision_state, 'manageable')
309
310        # Set retired without reason
311        node = self.conn.baremetal.update_node(node, is_retired=True)
312        self.assertTrue(node.is_retired)
313        self.assertIsNone(node.retired_reason)
314
315        # Verify set retired on server side
316        node = self.conn.baremetal.get_node(node.id)
317        self.assertTrue(node.is_retired)
318        self.assertIsNone(node.retired_reason)
319
320        # Add the reason
321        node = self.conn.baremetal.update_node(node, retired_reason=reason)
322        self.assertTrue(node.is_retired)
323        self.assertEqual(reason, node.retired_reason)
324
325        # Verify the reason on server side
326        node = self.conn.baremetal.get_node(node.id)
327        self.assertTrue(node.is_retired)
328        self.assertEqual(reason, node.retired_reason)
329
330        # Unset retired
331        node = self.conn.baremetal.update_node(node, is_retired=False)
332        self.assertFalse(node.is_retired)
333        self.assertIsNone(node.retired_reason)
334
335        # Verify on server side
336        node = self.conn.baremetal.get_node(node.id)
337        self.assertFalse(node.is_retired)
338        self.assertIsNone(node.retired_reason)
339
340        # Set retired with reason
341        node = self.conn.baremetal.update_node(node, is_retired=True,
342                                               retired_reason=reason)
343        self.assertTrue(node.is_retired)
344        self.assertEqual(reason, node.retired_reason)
345
346        # Verify on server side
347        node = self.conn.baremetal.get_node(node.id)
348        self.assertTrue(node.is_retired)
349        self.assertEqual(reason, node.retired_reason)
350
351
352class TestBareMetalNodeFields(base.BaseBaremetalTest):
353
354    min_microversion = '1.8'
355
356    def test_node_fields(self):
357        self.create_node()
358        result = self.conn.baremetal.nodes(
359            fields=['uuid', 'name', 'instance_id'])
360        for item in result:
361            self.assertIsNotNone(item.id)
362            self.assertIsNone(item.driver)
363
364
365class TestBareMetalVif(base.BaseBaremetalTest):
366
367    min_microversion = '1.28'
368
369    def setUp(self):
370        super(TestBareMetalVif, self).setUp()
371        self.node = self.create_node(network_interface='noop')
372        self.vif_id = "200712fc-fdfb-47da-89a6-2d19f76c7618"
373
374    def test_node_vif_attach_detach(self):
375        self.conn.baremetal.attach_vif_to_node(self.node, self.vif_id)
376        # NOTE(dtantsur): The noop networking driver is completely noop - the
377        # VIF list does not return anything of value.
378        self.conn.baremetal.list_node_vifs(self.node)
379        res = self.conn.baremetal.detach_vif_from_node(self.node, self.vif_id,
380                                                       ignore_missing=False)
381        self.assertTrue(res)
382
383    def test_node_vif_negative(self):
384        uuid = "5c9dcd04-2073-49bc-9618-99ae634d8971"
385        self.assertRaises(exceptions.ResourceNotFound,
386                          self.conn.baremetal.attach_vif_to_node,
387                          uuid, self.vif_id)
388        self.assertRaises(exceptions.ResourceNotFound,
389                          self.conn.baremetal.list_node_vifs,
390                          uuid)
391        self.assertRaises(exceptions.ResourceNotFound,
392                          self.conn.baremetal.detach_vif_from_node,
393                          uuid, self.vif_id, ignore_missing=False)
394
395
396class TestTraits(base.BaseBaremetalTest):
397
398    min_microversion = '1.37'
399
400    def setUp(self):
401        super(TestTraits, self).setUp()
402        self.node = self.create_node()
403
404    def test_add_remove_node_trait(self):
405        node = self.conn.baremetal.get_node(self.node)
406        self.assertEqual([], node.traits)
407
408        self.conn.baremetal.add_node_trait(self.node, 'CUSTOM_FAKE')
409        self.assertEqual(['CUSTOM_FAKE'], self.node.traits)
410        node = self.conn.baremetal.get_node(self.node)
411        self.assertEqual(['CUSTOM_FAKE'], node.traits)
412
413        self.conn.baremetal.add_node_trait(self.node, 'CUSTOM_REAL')
414        self.assertEqual(sorted(['CUSTOM_FAKE', 'CUSTOM_REAL']),
415                         sorted(self.node.traits))
416        node = self.conn.baremetal.get_node(self.node)
417        self.assertEqual(sorted(['CUSTOM_FAKE', 'CUSTOM_REAL']),
418                         sorted(node.traits))
419
420        self.conn.baremetal.remove_node_trait(node, 'CUSTOM_FAKE',
421                                              ignore_missing=False)
422        self.assertEqual(['CUSTOM_REAL'], self.node.traits)
423        node = self.conn.baremetal.get_node(self.node)
424        self.assertEqual(['CUSTOM_REAL'], node.traits)
425
426    def test_set_node_traits(self):
427        node = self.conn.baremetal.get_node(self.node)
428        self.assertEqual([], node.traits)
429
430        traits1 = ['CUSTOM_FAKE', 'CUSTOM_REAL']
431        traits2 = ['CUSTOM_FOOBAR']
432
433        self.conn.baremetal.set_node_traits(self.node, traits1)
434        self.assertEqual(sorted(traits1), sorted(self.node.traits))
435        node = self.conn.baremetal.get_node(self.node)
436        self.assertEqual(sorted(traits1), sorted(node.traits))
437
438        self.conn.baremetal.set_node_traits(self.node, traits2)
439        self.assertEqual(['CUSTOM_FOOBAR'], self.node.traits)
440        node = self.conn.baremetal.get_node(self.node)
441        self.assertEqual(['CUSTOM_FOOBAR'], node.traits)
442