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