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 13from unittest import mock 14 15from openstack.baremetal.v1 import _proxy 16from openstack.baremetal.v1 import allocation 17from openstack.baremetal.v1 import chassis 18from openstack.baremetal.v1 import driver 19from openstack.baremetal.v1 import node 20from openstack.baremetal.v1 import port 21from openstack.baremetal.v1 import port_group 22from openstack.baremetal.v1 import volume_connector 23from openstack.baremetal.v1 import volume_target 24from openstack import exceptions 25from openstack.tests.unit import base 26from openstack.tests.unit import test_proxy_base 27 28 29_MOCK_METHOD = 'openstack.baremetal.v1._proxy.Proxy._get_with_fields' 30 31 32class TestBaremetalProxy(test_proxy_base.TestProxyBase): 33 34 def setUp(self): 35 super(TestBaremetalProxy, self).setUp() 36 self.proxy = _proxy.Proxy(self.session) 37 38 def test_drivers(self): 39 self.verify_list(self.proxy.drivers, driver.Driver) 40 41 def test_get_driver(self): 42 self.verify_get(self.proxy.get_driver, driver.Driver) 43 44 @mock.patch.object(chassis.Chassis, 'list') 45 def test_chassis_detailed(self, mock_list): 46 result = self.proxy.chassis(details=True, query=1) 47 self.assertIs(result, mock_list.return_value) 48 mock_list.assert_called_once_with(self.proxy, details=True, query=1) 49 50 @mock.patch.object(chassis.Chassis, 'list') 51 def test_chassis_not_detailed(self, mock_list): 52 result = self.proxy.chassis(query=1) 53 self.assertIs(result, mock_list.return_value) 54 mock_list.assert_called_once_with(self.proxy, details=False, query=1) 55 56 def test_create_chassis(self): 57 self.verify_create(self.proxy.create_chassis, chassis.Chassis) 58 59 def test_find_chassis(self): 60 self.verify_find(self.proxy.find_chassis, chassis.Chassis) 61 62 def test_get_chassis(self): 63 self.verify_get(self.proxy.get_chassis, chassis.Chassis, 64 mock_method=_MOCK_METHOD, 65 expected_kwargs={'fields': None}) 66 67 def test_update_chassis(self): 68 self.verify_update(self.proxy.update_chassis, chassis.Chassis) 69 70 def test_delete_chassis(self): 71 self.verify_delete(self.proxy.delete_chassis, chassis.Chassis, False) 72 73 def test_delete_chassis_ignore(self): 74 self.verify_delete(self.proxy.delete_chassis, chassis.Chassis, True) 75 76 @mock.patch.object(node.Node, 'list') 77 def test_nodes_detailed(self, mock_list): 78 result = self.proxy.nodes(details=True, query=1) 79 self.assertIs(result, mock_list.return_value) 80 mock_list.assert_called_once_with(self.proxy, details=True, query=1) 81 82 @mock.patch.object(node.Node, 'list') 83 def test_nodes_not_detailed(self, mock_list): 84 result = self.proxy.nodes(query=1) 85 self.assertIs(result, mock_list.return_value) 86 mock_list.assert_called_once_with(self.proxy, details=False, query=1) 87 88 def test_create_node(self): 89 self.verify_create(self.proxy.create_node, node.Node) 90 91 def test_find_node(self): 92 self.verify_find(self.proxy.find_node, node.Node) 93 94 def test_get_node(self): 95 self.verify_get(self.proxy.get_node, node.Node, 96 mock_method=_MOCK_METHOD, 97 expected_kwargs={'fields': None}) 98 99 @mock.patch.object(node.Node, 'commit', autospec=True) 100 def test_update_node(self, mock_commit): 101 self.proxy.update_node('uuid', instance_id='new value') 102 mock_commit.assert_called_once_with(mock.ANY, self.proxy, 103 retry_on_conflict=True) 104 self.assertEqual('new value', mock_commit.call_args[0][0].instance_id) 105 106 @mock.patch.object(node.Node, 'commit', autospec=True) 107 def test_update_node_no_retries(self, mock_commit): 108 self.proxy.update_node('uuid', instance_id='new value', 109 retry_on_conflict=False) 110 mock_commit.assert_called_once_with(mock.ANY, self.proxy, 111 retry_on_conflict=False) 112 self.assertEqual('new value', mock_commit.call_args[0][0].instance_id) 113 114 def test_delete_node(self): 115 self.verify_delete(self.proxy.delete_node, node.Node, False) 116 117 def test_delete_node_ignore(self): 118 self.verify_delete(self.proxy.delete_node, node.Node, True) 119 120 @mock.patch.object(port.Port, 'list') 121 def test_ports_detailed(self, mock_list): 122 result = self.proxy.ports(details=True, query=1) 123 self.assertIs(result, mock_list.return_value) 124 mock_list.assert_called_once_with(self.proxy, details=True, query=1) 125 126 @mock.patch.object(port.Port, 'list') 127 def test_ports_not_detailed(self, mock_list): 128 result = self.proxy.ports(query=1) 129 self.assertIs(result, mock_list.return_value) 130 mock_list.assert_called_once_with(self.proxy, details=False, query=1) 131 132 def test_create_port(self): 133 self.verify_create(self.proxy.create_port, port.Port) 134 135 def test_find_port(self): 136 self.verify_find(self.proxy.find_port, port.Port) 137 138 def test_get_port(self): 139 self.verify_get(self.proxy.get_port, port.Port, 140 mock_method=_MOCK_METHOD, 141 expected_kwargs={'fields': None}) 142 143 def test_update_port(self): 144 self.verify_update(self.proxy.update_port, port.Port) 145 146 def test_delete_port(self): 147 self.verify_delete(self.proxy.delete_port, port.Port, False) 148 149 def test_delete_port_ignore(self): 150 self.verify_delete(self.proxy.delete_port, port.Port, True) 151 152 @mock.patch.object(port_group.PortGroup, 'list') 153 def test_port_groups_detailed(self, mock_list): 154 result = self.proxy.port_groups(details=True, query=1) 155 self.assertIs(result, mock_list.return_value) 156 mock_list.assert_called_once_with(self.proxy, details=True, query=1) 157 158 @mock.patch.object(port_group.PortGroup, 'list') 159 def test_port_groups_not_detailed(self, mock_list): 160 result = self.proxy.port_groups(query=1) 161 self.assertIs(result, mock_list.return_value) 162 mock_list.assert_called_once_with(self.proxy, details=False, query=1) 163 164 def test_get_port_group(self): 165 self.verify_get(self.proxy.get_port_group, port_group.PortGroup, 166 mock_method=_MOCK_METHOD, 167 expected_kwargs={'fields': None}) 168 169 def test_create_allocation(self): 170 self.verify_create(self.proxy.create_allocation, allocation.Allocation) 171 172 def test_get_allocation(self): 173 self.verify_get(self.proxy.get_allocation, allocation.Allocation, 174 mock_method=_MOCK_METHOD, 175 expected_kwargs={'fields': None}) 176 177 def test_delete_allocation(self): 178 self.verify_delete(self.proxy.delete_allocation, allocation.Allocation, 179 False) 180 181 def test_delete_allocation_ignore(self): 182 self.verify_delete(self.proxy.delete_allocation, allocation.Allocation, 183 True) 184 185 def test_create_volume_connector(self): 186 self.verify_create(self.proxy.create_volume_connector, 187 volume_connector.VolumeConnector) 188 189 def test_find_volume_connector(self): 190 self.verify_find(self.proxy.find_volume_connector, 191 volume_connector.VolumeConnector) 192 193 def test_get_volume_connector(self): 194 self.verify_get(self.proxy.get_volume_connector, 195 volume_connector.VolumeConnector, 196 mock_method=_MOCK_METHOD, 197 expected_kwargs={'fields': None}) 198 199 def test_delete_volume_connector(self): 200 self.verify_delete(self.proxy.delete_volume_connector, 201 volume_connector.VolumeConnector, 202 False) 203 204 def test_delete_volume_connector_ignore(self): 205 self.verify_delete(self.proxy.delete_volume_connector, 206 volume_connector.VolumeConnector, 207 True) 208 209 @mock.patch.object(volume_target.VolumeTarget, 'list') 210 def test_volume_target_detailed(self, mock_list): 211 result = self.proxy.volume_targets(details=True, query=1) 212 self.assertIs(result, mock_list.return_value) 213 mock_list.assert_called_once_with(self.proxy, detail=True, query=1) 214 215 @mock.patch.object(volume_target.VolumeTarget, 'list') 216 def test_volume_target_not_detailed(self, mock_list): 217 result = self.proxy.volume_targets(query=1) 218 self.assertIs(result, mock_list.return_value) 219 mock_list.assert_called_once_with(self.proxy, query=1) 220 221 def test_create_volume_target(self): 222 self.verify_create(self.proxy.create_volume_target, 223 volume_target.VolumeTarget) 224 225 def test_find_volume_target(self): 226 self.verify_find(self.proxy.find_volume_target, 227 volume_target.VolumeTarget) 228 229 def test_get_volume_target(self): 230 self.verify_get(self.proxy.get_volume_target, 231 volume_target.VolumeTarget, 232 mock_method=_MOCK_METHOD, 233 expected_kwargs={'fields': None}) 234 235 def test_delete_volume_target(self): 236 self.verify_delete(self.proxy.delete_volume_target, 237 volume_target.VolumeTarget, 238 False) 239 240 def test_delete_volume_target_ignore(self): 241 self.verify_delete(self.proxy.delete_volume_target, 242 volume_target.VolumeTarget, 243 True) 244 245 @mock.patch.object(node.Node, 'fetch', autospec=True) 246 def test__get_with_fields_none(self, mock_fetch): 247 result = self.proxy._get_with_fields(node.Node, 'value') 248 self.assertIs(result, mock_fetch.return_value) 249 mock_fetch.assert_called_once_with(mock.ANY, self.proxy, 250 error_message=mock.ANY) 251 252 @mock.patch.object(node.Node, 'fetch', autospec=True) 253 def test__get_with_fields_node(self, mock_fetch): 254 result = self.proxy._get_with_fields( 255 # Mix of server-side and client-side fields 256 node.Node, 'value', fields=['maintenance', 'id', 'instance_id']) 257 self.assertIs(result, mock_fetch.return_value) 258 mock_fetch.assert_called_once_with( 259 mock.ANY, self.proxy, error_message=mock.ANY, 260 # instance_id converted to server-side instance_uuid 261 fields='maintenance,uuid,instance_uuid') 262 263 @mock.patch.object(port.Port, 'fetch', autospec=True) 264 def test__get_with_fields_port(self, mock_fetch): 265 result = self.proxy._get_with_fields( 266 port.Port, 'value', fields=['address', 'id', 'node_id']) 267 self.assertIs(result, mock_fetch.return_value) 268 mock_fetch.assert_called_once_with( 269 mock.ANY, self.proxy, error_message=mock.ANY, 270 # node_id converted to server-side node_uuid 271 fields='address,uuid,node_uuid') 272 273 274@mock.patch('time.sleep', lambda _sec: None) 275@mock.patch.object(_proxy.Proxy, 'get_node', autospec=True) 276class TestWaitForNodesProvisionState(base.TestCase): 277 278 def setUp(self): 279 super(TestWaitForNodesProvisionState, self).setUp() 280 self.session = mock.Mock() 281 self.proxy = _proxy.Proxy(self.session) 282 283 def test_success(self, mock_get): 284 # two attempts, one node succeeds after the 1st 285 nodes = [mock.Mock(spec=node.Node, id=str(i)) 286 for i in range(3)] 287 for i, n in enumerate(nodes): 288 # 1st attempt on 1st node, 2nd attempt on 2nd node 289 n._check_state_reached.return_value = not (i % 2) 290 mock_get.side_effect = nodes 291 292 result = self.proxy.wait_for_nodes_provision_state( 293 ['abcd', node.Node(id='1234')], 'fake state') 294 self.assertEqual([nodes[0], nodes[2]], result) 295 296 for n in nodes: 297 n._check_state_reached.assert_called_once_with( 298 self.proxy, 'fake state', True) 299 300 def test_success_no_fail(self, mock_get): 301 # two attempts, one node succeeds after the 1st 302 nodes = [mock.Mock(spec=node.Node, id=str(i)) 303 for i in range(3)] 304 for i, n in enumerate(nodes): 305 # 1st attempt on 1st node, 2nd attempt on 2nd node 306 n._check_state_reached.return_value = not (i % 2) 307 mock_get.side_effect = nodes 308 309 result = self.proxy.wait_for_nodes_provision_state( 310 ['abcd', node.Node(id='1234')], 'fake state', fail=False) 311 self.assertEqual([nodes[0], nodes[2]], result.success) 312 self.assertEqual([], result.failure) 313 self.assertEqual([], result.timeout) 314 315 for n in nodes: 316 n._check_state_reached.assert_called_once_with( 317 self.proxy, 'fake state', True) 318 319 def test_timeout(self, mock_get): 320 mock_get.return_value._check_state_reached.return_value = False 321 mock_get.return_value.id = '1234' 322 323 self.assertRaises(exceptions.ResourceTimeout, 324 self.proxy.wait_for_nodes_provision_state, 325 ['abcd', node.Node(id='1234')], 'fake state', 326 timeout=0.001) 327 mock_get.return_value._check_state_reached.assert_called_with( 328 self.proxy, 'fake state', True) 329 330 def test_timeout_no_fail(self, mock_get): 331 mock_get.return_value._check_state_reached.return_value = False 332 mock_get.return_value.id = '1234' 333 334 result = self.proxy.wait_for_nodes_provision_state( 335 ['abcd'], 'fake state', timeout=0.001, fail=False) 336 mock_get.return_value._check_state_reached.assert_called_with( 337 self.proxy, 'fake state', True) 338 339 self.assertEqual([], result.success) 340 self.assertEqual([mock_get.return_value], result.timeout) 341 self.assertEqual([], result.failure) 342 343 def test_timeout_and_failures_not_fail(self, mock_get): 344 def _fake_get(_self, node): 345 result = mock.Mock() 346 result.id = getattr(node, 'id', node) 347 if result.id == '1': 348 result._check_state_reached.return_value = True 349 elif result.id == '2': 350 result._check_state_reached.side_effect = \ 351 exceptions.ResourceFailure("boom") 352 else: 353 result._check_state_reached.return_value = False 354 return result 355 356 mock_get.side_effect = _fake_get 357 358 result = self.proxy.wait_for_nodes_provision_state( 359 ['1', '2', '3'], 'fake state', timeout=0.001, fail=False) 360 361 self.assertEqual(['1'], [x.id for x in result.success]) 362 self.assertEqual(['3'], [x.id for x in result.timeout]) 363 self.assertEqual(['2'], [x.id for x in result.failure]) 364