1# Copyright 2011 OpenStack Foundation 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16"""Tests For miscellaneous util methods used with volume.""" 17 18 19import datetime 20import io 21import mock 22import six 23 24from castellan import key_manager 25import ddt 26from oslo_concurrency import processutils 27from oslo_config import cfg 28from oslo_utils import units 29 30from cinder import context 31from cinder import db 32from cinder.db.sqlalchemy import models 33from cinder import exception 34from cinder.objects import fields 35from cinder import test 36from cinder.tests.unit.backup import fake_backup 37from cinder.tests.unit import fake_constants as fake 38from cinder.tests.unit import fake_group 39from cinder.tests.unit import fake_snapshot 40from cinder.tests.unit import fake_volume 41from cinder import utils 42from cinder.volume import throttling 43from cinder.volume import utils as volume_utils 44from cinder.volume import volume_types 45 46 47CONF = cfg.CONF 48 49 50class NotifyUsageTestCase(test.TestCase): 51 @mock.patch('cinder.volume.utils._usage_from_volume') 52 @mock.patch('cinder.volume.utils.CONF') 53 @mock.patch('cinder.volume.utils.rpc') 54 def test_notify_about_volume_usage(self, mock_rpc, mock_conf, mock_usage): 55 mock_conf.host = 'host1' 56 output = volume_utils.notify_about_volume_usage(mock.sentinel.context, 57 mock.sentinel.volume, 58 'test_suffix') 59 self.assertIsNone(output) 60 mock_usage.assert_called_once_with(mock.sentinel.context, 61 mock.sentinel.volume) 62 mock_rpc.get_notifier.assert_called_once_with('volume', 'host1') 63 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 64 mock.sentinel.context, 65 'volume.test_suffix', 66 mock_usage.return_value) 67 68 @mock.patch('cinder.volume.utils._usage_from_volume') 69 @mock.patch('cinder.volume.utils.CONF') 70 @mock.patch('cinder.volume.utils.rpc') 71 def test_notify_about_volume_usage_with_kwargs(self, mock_rpc, mock_conf, 72 mock_usage): 73 mock_conf.host = 'host1' 74 output = volume_utils.notify_about_volume_usage( 75 mock.sentinel.context, 76 mock.sentinel.volume, 77 'test_suffix', 78 extra_usage_info={'a': 'b', 'c': 'd'}, 79 host='host2') 80 self.assertIsNone(output) 81 mock_usage.assert_called_once_with(mock.sentinel.context, 82 mock.sentinel.volume, a='b', c='d') 83 mock_rpc.get_notifier.assert_called_once_with('volume', 'host2') 84 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 85 mock.sentinel.context, 86 'volume.test_suffix', 87 mock_usage.return_value) 88 89 @mock.patch('cinder.volume.utils._usage_from_snapshot') 90 @mock.patch('cinder.volume.utils.CONF') 91 @mock.patch('cinder.volume.utils.rpc') 92 def test_notify_about_snapshot_usage(self, mock_rpc, 93 mock_conf, mock_usage): 94 mock_conf.host = 'host1' 95 output = volume_utils.notify_about_snapshot_usage( 96 mock.sentinel.context, 97 mock.sentinel.snapshot, 98 'test_suffix') 99 self.assertIsNone(output) 100 mock_usage.assert_called_once_with(mock.sentinel.snapshot, 101 mock.sentinel.context) 102 mock_rpc.get_notifier.assert_called_once_with('snapshot', 'host1') 103 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 104 mock.sentinel.context, 105 'snapshot.test_suffix', 106 mock_usage.return_value) 107 108 @mock.patch('cinder.volume.utils._usage_from_snapshot') 109 @mock.patch('cinder.volume.utils.CONF') 110 @mock.patch('cinder.volume.utils.rpc') 111 def test_notify_about_snapshot_usage_with_kwargs(self, mock_rpc, mock_conf, 112 mock_usage): 113 mock_conf.host = 'host1' 114 output = volume_utils.notify_about_snapshot_usage( 115 mock.sentinel.context, 116 mock.sentinel.snapshot, 117 'test_suffix', 118 extra_usage_info={'a': 'b', 'c': 'd'}, 119 host='host2') 120 self.assertIsNone(output) 121 mock_usage.assert_called_once_with(mock.sentinel.snapshot, 122 mock.sentinel.context, 123 a='b', c='d') 124 mock_rpc.get_notifier.assert_called_once_with('snapshot', 'host2') 125 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 126 mock.sentinel.context, 127 'snapshot.test_suffix', 128 mock_usage.return_value) 129 130 @mock.patch('cinder.db.volume_get') 131 def test_usage_from_snapshot(self, volume_get): 132 raw_volume = { 133 'id': fake.VOLUME_ID, 134 'availability_zone': 'nova' 135 } 136 ctxt = context.get_admin_context() 137 volume_obj = fake_volume.fake_volume_obj(ctxt, **raw_volume) 138 volume_get.return_value = volume_obj 139 raw_snapshot = { 140 'project_id': fake.PROJECT_ID, 141 'user_id': fake.USER_ID, 142 'volume': volume_obj, 143 'volume_id': fake.VOLUME_ID, 144 'volume_size': 1, 145 'id': fake.SNAPSHOT_ID, 146 'display_name': '11', 147 'created_at': '2014-12-11T10:10:00', 148 'status': fields.SnapshotStatus.ERROR, 149 'deleted': '', 150 'snapshot_metadata': [{'key': 'fake_snap_meta_key', 151 'value': 'fake_snap_meta_value'}], 152 'expected_attrs': ['metadata'], 153 } 154 155 snapshot_obj = fake_snapshot.fake_snapshot_obj(ctxt, **raw_snapshot) 156 usage_info = volume_utils._usage_from_snapshot(snapshot_obj, ctxt) 157 expected_snapshot = { 158 'tenant_id': fake.PROJECT_ID, 159 'user_id': fake.USER_ID, 160 'availability_zone': 'nova', 161 'volume_id': fake.VOLUME_ID, 162 'volume_size': 1, 163 'snapshot_id': fake.SNAPSHOT_ID, 164 'display_name': '11', 165 'created_at': '2014-12-11T10:10:00+00:00', 166 'status': fields.SnapshotStatus.ERROR, 167 'deleted': '', 168 'metadata': six.text_type({'fake_snap_meta_key': 169 u'fake_snap_meta_value'}), 170 } 171 self.assertDictEqual(expected_snapshot, usage_info) 172 173 @mock.patch('cinder.db.volume_get') 174 def test_usage_from_deleted_snapshot(self, volume_get): 175 raw_volume = { 176 'id': fake.VOLUME_ID, 177 'availability_zone': 'nova', 178 'deleted': 1 179 } 180 ctxt = context.get_admin_context() 181 volume_obj = fake_volume.fake_volume_obj(ctxt, **raw_volume) 182 volume_get.return_value = volume_obj 183 184 raw_snapshot = { 185 'project_id': fake.PROJECT_ID, 186 'user_id': fake.USER_ID, 187 'volume': volume_obj, 188 'volume_id': fake.VOLUME_ID, 189 'volume_size': 1, 190 'id': fake.SNAPSHOT_ID, 191 'display_name': '11', 192 'created_at': '2014-12-11T10:10:00', 193 'status': fields.SnapshotStatus.ERROR, 194 'deleted': '', 195 'snapshot_metadata': [{'key': 'fake_snap_meta_key', 196 'value': 'fake_snap_meta_value'}], 197 'expected_attrs': ['metadata'], 198 } 199 200 snapshot_obj = fake_snapshot.fake_snapshot_obj(ctxt, **raw_snapshot) 201 usage_info = volume_utils._usage_from_snapshot(snapshot_obj, ctxt) 202 expected_snapshot = { 203 'tenant_id': fake.PROJECT_ID, 204 'user_id': fake.USER_ID, 205 'availability_zone': 'nova', 206 'volume_id': fake.VOLUME_ID, 207 'volume_size': 1, 208 'snapshot_id': fake.SNAPSHOT_ID, 209 'display_name': '11', 210 'created_at': mock.ANY, 211 'status': fields.SnapshotStatus.ERROR, 212 'deleted': '', 213 'metadata': six.text_type({'fake_snap_meta_key': 214 u'fake_snap_meta_value'}), 215 } 216 self.assertDictEqual(expected_snapshot, usage_info) 217 218 @mock.patch('cinder.db.volume_glance_metadata_get') 219 @mock.patch('cinder.db.volume_attachment_get_all_by_volume_id') 220 def test_usage_from_volume(self, mock_attachment, mock_image_metadata): 221 mock_image_metadata.return_value = {'image_id': 'fake_image_id'} 222 mock_attachment.return_value = [{'instance_uuid': 'fake_instance_id'}] 223 raw_volume = { 224 'project_id': '12b0330ec2584a', 225 'user_id': '158cba1b8c2bb6008e', 226 'host': 'fake_host', 227 'availability_zone': 'nova', 228 'volume_type_id': 'fake_volume_type_id', 229 'id': 'fake_volume_id', 230 'size': 1, 231 'display_name': 'test_volume', 232 'created_at': datetime.datetime(2015, 1, 1, 1, 1, 1), 233 'launched_at': datetime.datetime(2015, 1, 1, 1, 1, 1), 234 'snapshot_id': None, 235 'replication_status': None, 236 'replication_extended_status': None, 237 'replication_driver_data': None, 238 'status': 'available', 239 'volume_metadata': {'fake_metadata_key': 'fake_metadata_value'}, 240 } 241 usage_info = volume_utils._usage_from_volume( 242 mock.sentinel.context, 243 raw_volume) 244 expected_volume = { 245 'tenant_id': '12b0330ec2584a', 246 'user_id': '158cba1b8c2bb6008e', 247 'host': 'fake_host', 248 'availability_zone': 'nova', 249 'volume_type': 'fake_volume_type_id', 250 'volume_id': 'fake_volume_id', 251 'size': 1, 252 'display_name': 'test_volume', 253 'created_at': '2015-01-01T01:01:01', 254 'launched_at': '2015-01-01T01:01:01', 255 'snapshot_id': None, 256 'replication_status': None, 257 'replication_extended_status': None, 258 'replication_driver_data': None, 259 'status': 'available', 260 'metadata': {'fake_metadata_key': 'fake_metadata_value'}, 261 'glance_metadata': {'image_id': 'fake_image_id'}, 262 'volume_attachment': [{'instance_uuid': 'fake_instance_id'}], 263 } 264 self.assertEqual(expected_volume, usage_info) 265 266 @mock.patch('cinder.volume.utils._usage_from_consistencygroup') 267 @mock.patch('cinder.volume.utils.CONF') 268 @mock.patch('cinder.volume.utils.rpc') 269 def test_notify_about_consistencygroup_usage(self, mock_rpc, 270 mock_conf, mock_usage): 271 mock_conf.host = 'host1' 272 output = volume_utils.notify_about_consistencygroup_usage( 273 mock.sentinel.context, 274 mock.sentinel.consistencygroup, 275 'test_suffix') 276 self.assertIsNone(output) 277 mock_usage.assert_called_once_with(mock.sentinel.consistencygroup) 278 mock_rpc.get_notifier.assert_called_once_with('consistencygroup', 279 'host1') 280 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 281 mock.sentinel.context, 282 'consistencygroup.test_suffix', 283 mock_usage.return_value) 284 285 @mock.patch('cinder.volume.utils._usage_from_consistencygroup') 286 @mock.patch('cinder.volume.utils.CONF') 287 @mock.patch('cinder.volume.utils.rpc') 288 def test_notify_about_consistencygroup_usage_with_kwargs(self, mock_rpc, 289 mock_conf, 290 mock_usage): 291 mock_conf.host = 'host1' 292 output = volume_utils.notify_about_consistencygroup_usage( 293 mock.sentinel.context, 294 mock.sentinel.consistencygroup, 295 'test_suffix', 296 extra_usage_info={'a': 'b', 'c': 'd'}, 297 host='host2') 298 self.assertIsNone(output) 299 mock_usage.assert_called_once_with(mock.sentinel.consistencygroup, 300 a='b', c='d') 301 mock_rpc.get_notifier.assert_called_once_with('consistencygroup', 302 'host2') 303 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 304 mock.sentinel.context, 305 'consistencygroup.test_suffix', 306 mock_usage.return_value) 307 308 @mock.patch('cinder.volume.utils._usage_from_cgsnapshot') 309 @mock.patch('cinder.volume.utils.CONF') 310 @mock.patch('cinder.volume.utils.rpc') 311 def test_notify_about_cgsnapshot_usage(self, mock_rpc, 312 mock_conf, mock_usage): 313 mock_conf.host = 'host1' 314 output = volume_utils.notify_about_cgsnapshot_usage( 315 mock.sentinel.context, 316 mock.sentinel.cgsnapshot, 317 'test_suffix') 318 self.assertIsNone(output) 319 mock_usage.assert_called_once_with(mock.sentinel.cgsnapshot) 320 mock_rpc.get_notifier.assert_called_once_with('cgsnapshot', 'host1') 321 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 322 mock.sentinel.context, 323 'cgsnapshot.test_suffix', 324 mock_usage.return_value) 325 326 @mock.patch('cinder.volume.utils._usage_from_cgsnapshot') 327 @mock.patch('cinder.volume.utils.CONF') 328 @mock.patch('cinder.volume.utils.rpc') 329 def test_notify_about_cgsnapshot_usage_with_kwargs(self, mock_rpc, 330 mock_conf, mock_usage): 331 mock_conf.host = 'host1' 332 output = volume_utils.notify_about_cgsnapshot_usage( 333 mock.sentinel.context, 334 mock.sentinel.cgsnapshot, 335 'test_suffix', 336 extra_usage_info={'a': 'b', 'c': 'd'}, 337 host='host2') 338 self.assertIsNone(output) 339 mock_usage.assert_called_once_with(mock.sentinel.cgsnapshot, 340 a='b', c='d') 341 mock_rpc.get_notifier.assert_called_once_with('cgsnapshot', 'host2') 342 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 343 mock.sentinel.context, 344 'cgsnapshot.test_suffix', 345 mock_usage.return_value) 346 347 def test_usage_from_backup(self): 348 raw_backup = { 349 'project_id': fake.PROJECT_ID, 350 'user_id': fake.USER_ID, 351 'availability_zone': 'nova', 352 'id': fake.BACKUP_ID, 353 'host': 'fake_host', 354 'display_name': 'test_backup', 355 'created_at': datetime.datetime(2015, 1, 1, 1, 1, 1), 356 'status': 'available', 357 'volume_id': fake.VOLUME_ID, 358 'size': 1, 359 'service_metadata': None, 360 'service': 'cinder.backup.drivers.swift', 361 'fail_reason': None, 362 'parent_id': fake.BACKUP2_ID, 363 'num_dependent_backups': 0, 364 'snapshot_id': None, 365 } 366 367 ctxt = context.get_admin_context() 368 backup_obj = fake_backup.fake_backup_obj(ctxt, **raw_backup) 369 370 # Make it easier to find out differences between raw and expected. 371 expected_backup = raw_backup.copy() 372 expected_backup['tenant_id'] = expected_backup.pop('project_id') 373 expected_backup['backup_id'] = expected_backup.pop('id') 374 expected_backup['created_at'] = '2015-01-01T01:01:01+00:00' 375 376 usage_info = volume_utils._usage_from_backup(backup_obj) 377 self.assertDictEqual(expected_backup, usage_info) 378 379 380class LVMVolumeDriverTestCase(test.TestCase): 381 def test_convert_blocksize_option(self): 382 # Test valid volume_dd_blocksize 383 bs = volume_utils._check_blocksize('10M') 384 self.assertEqual('10M', bs) 385 386 bs = volume_utils._check_blocksize('1xBBB') 387 self.assertEqual('1M', bs) 388 389 # Test 'volume_dd_blocksize' with fraction 390 bs = volume_utils._check_blocksize('1.3M') 391 self.assertEqual('1M', bs) 392 393 # Test zero-size 'volume_dd_blocksize' 394 bs = volume_utils._check_blocksize('0M') 395 self.assertEqual('1M', bs) 396 397 # Test negative 'volume_dd_blocksize' 398 bs = volume_utils._check_blocksize('-1M') 399 self.assertEqual('1M', bs) 400 401 # Test non-digital 'volume_dd_blocksize' 402 bs = volume_utils._check_blocksize('ABM') 403 self.assertEqual('1M', bs) 404 405 @mock.patch('cinder.volume.utils._usage_from_capacity') 406 @mock.patch('cinder.volume.utils.CONF') 407 @mock.patch('cinder.volume.utils.rpc') 408 def test_notify_about_capacity_usage(self, mock_rpc, 409 mock_conf, mock_usage): 410 mock_conf.host = 'host1' 411 output = volume_utils.notify_about_capacity_usage( 412 mock.sentinel.context, 413 mock.sentinel.capacity, 414 'test_suffix') 415 self.assertIsNone(output) 416 mock_usage.assert_called_once_with(mock.sentinel.capacity) 417 mock_rpc.get_notifier.assert_called_once_with('capacity', 'host1') 418 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 419 mock.sentinel.context, 420 'capacity.test_suffix', 421 mock_usage.return_value) 422 423 @mock.patch('cinder.volume.utils._usage_from_capacity') 424 @mock.patch('cinder.volume.utils.CONF') 425 @mock.patch('cinder.volume.utils.rpc') 426 def test_notify_about_capacity_usage_with_kwargs(self, mock_rpc, mock_conf, 427 mock_usage): 428 mock_conf.host = 'host1' 429 output = volume_utils.notify_about_capacity_usage( 430 mock.sentinel.context, 431 mock.sentinel.capacity, 432 'test_suffix', 433 extra_usage_info={'a': 'b', 'c': 'd'}, 434 host='host2') 435 self.assertIsNone(output) 436 mock_usage.assert_called_once_with(mock.sentinel.capacity, 437 a='b', c='d') 438 mock_rpc.get_notifier.assert_called_once_with('capacity', 'host2') 439 mock_rpc.get_notifier.return_value.info.assert_called_once_with( 440 mock.sentinel.context, 441 'capacity.test_suffix', 442 mock_usage.return_value) 443 444 def test_usage_from_capacity(self): 445 test_capacity = { 446 'name_to_id': 'host1@backend1#pool1', 447 'type': 'pool', 448 'total': '10.01', 449 'free': '8.01', 450 'allocated': '2', 451 'provisioned': '2', 452 'virtual_free': '8.01', 453 'reported_at': '2014-12-11T10:10:00', 454 } 455 456 usage_info = volume_utils._usage_from_capacity( 457 test_capacity) 458 expected_capacity = { 459 'name_to_id': 'host1@backend1#pool1', 460 'total': '10.01', 461 'free': '8.01', 462 'allocated': '2', 463 'provisioned': '2', 464 'virtual_free': '8.01', 465 'reported_at': '2014-12-11T10:10:00', 466 } 467 self.assertEqual(expected_capacity, usage_info) 468 469 470class OdirectSupportTestCase(test.TestCase): 471 @mock.patch('cinder.utils.execute') 472 def test_check_for_odirect_support(self, mock_exec): 473 output = volume_utils.check_for_odirect_support('/dev/abc', '/dev/def') 474 self.assertTrue(output) 475 mock_exec.assert_called_once_with('dd', 'count=0', 'if=/dev/abc', 476 'of=/dev/def', 'oflag=direct', 477 run_as_root=True) 478 mock_exec.reset_mock() 479 480 output = volume_utils.check_for_odirect_support('/dev/abc', '/dev/def', 481 'iflag=direct') 482 self.assertTrue(output) 483 mock_exec.assert_called_once_with('dd', 'count=0', 'if=/dev/abc', 484 'of=/dev/def', 'iflag=direct', 485 run_as_root=True) 486 mock_exec.reset_mock() 487 488 output = volume_utils.check_for_odirect_support('/dev/zero', 489 '/dev/def', 490 'iflag=direct') 491 self.assertFalse(output) 492 mock_exec.reset_mock() 493 494 output = volume_utils.check_for_odirect_support('/dev/zero', 495 '/dev/def') 496 self.assertTrue(output) 497 mock_exec.assert_called_once_with('dd', 'count=0', 'if=/dev/zero', 498 'of=/dev/def', 'oflag=direct', 499 run_as_root=True) 500 501 @mock.patch('cinder.utils.execute', 502 side_effect=processutils.ProcessExecutionError) 503 def test_check_for_odirect_support_error(self, mock_exec): 504 output = volume_utils.check_for_odirect_support('/dev/abc', '/dev/def') 505 self.assertFalse(output) 506 mock_exec.assert_called_once_with('dd', 'count=0', 'if=/dev/abc', 507 'of=/dev/def', 'oflag=direct', 508 run_as_root=True) 509 mock_exec.reset_mock() 510 output = volume_utils.check_for_odirect_support('/dev/zero', 511 '/dev/def') 512 self.assertFalse(output) 513 mock_exec.assert_called_once_with('dd', 'count=0', 'if=/dev/zero', 514 'of=/dev/def', 'oflag=direct', 515 run_as_root=True) 516 517 518class ClearVolumeTestCase(test.TestCase): 519 @mock.patch('cinder.volume.utils.copy_volume', return_value=None) 520 @mock.patch('cinder.volume.utils.CONF') 521 def test_clear_volume_conf(self, mock_conf, mock_copy): 522 mock_conf.volume_clear = 'zero' 523 mock_conf.volume_clear_size = 0 524 mock_conf.volume_dd_blocksize = '1M' 525 mock_conf.volume_clear_ionice = '-c3' 526 output = volume_utils.clear_volume(1024, 'volume_path') 527 self.assertIsNone(output) 528 mock_copy.assert_called_once_with('/dev/zero', 'volume_path', 1024, 529 '1M', sync=True, 530 execute=utils.execute, ionice='-c3', 531 throttle=None, sparse=False) 532 533 @mock.patch('cinder.volume.utils.copy_volume', return_value=None) 534 @mock.patch('cinder.volume.utils.CONF') 535 def test_clear_volume_args(self, mock_conf, mock_copy): 536 mock_conf.volume_clear = 'should_override_with_arg' 537 mock_conf.volume_clear_size = 0 538 mock_conf.volume_dd_blocksize = '1M' 539 mock_conf.volume_clear_ionice = '-c3' 540 output = volume_utils.clear_volume(1024, 'volume_path', 'zero', 1, 541 '-c0') 542 self.assertIsNone(output) 543 mock_copy.assert_called_once_with('/dev/zero', 'volume_path', 1, 544 '1M', sync=True, 545 execute=utils.execute, ionice='-c0', 546 throttle=None, sparse=False) 547 548 @mock.patch('cinder.volume.utils.CONF') 549 def test_clear_volume_invalid_opt(self, mock_conf): 550 mock_conf.volume_clear = 'non_existent_volume_clearer' 551 mock_conf.volume_clear_size = 0 552 mock_conf.volume_clear_ionice = None 553 self.assertRaises(exception.InvalidConfigurationValue, 554 volume_utils.clear_volume, 555 1024, "volume_path") 556 557 558class CopyVolumeTestCase(test.TestCase): 559 @mock.patch('cinder.volume.utils.check_for_odirect_support', 560 return_value=True) 561 @mock.patch('cinder.utils.execute') 562 @mock.patch('cinder.volume.utils.CONF') 563 def test_copy_volume_dd_iflag_and_oflag(self, mock_conf, mock_exec, 564 mock_support): 565 fake_throttle = throttling.Throttle(['fake_throttle']) 566 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 567 sync=True, execute=utils.execute, 568 ionice=None, throttle=fake_throttle) 569 self.assertIsNone(output) 570 mock_exec.assert_called_once_with('fake_throttle', 'dd', 571 'if=/dev/zero', 572 'of=/dev/null', 573 'count=%s' % units.Gi, 574 'bs=3M', 'iflag=count_bytes,direct', 575 'oflag=direct', run_as_root=True) 576 577 mock_exec.reset_mock() 578 579 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 580 sync=False, execute=utils.execute, 581 ionice=None, throttle=fake_throttle) 582 self.assertIsNone(output) 583 mock_exec.assert_called_once_with('fake_throttle', 'dd', 584 'if=/dev/zero', 585 'of=/dev/null', 586 'count=%s' % units.Gi, 587 'bs=3M', 'iflag=count_bytes,direct', 588 'oflag=direct', run_as_root=True) 589 590 @mock.patch('cinder.volume.utils.check_for_odirect_support', 591 return_value=False) 592 @mock.patch('cinder.utils.execute') 593 def test_copy_volume_dd_no_iflag_or_oflag(self, mock_exec, mock_support): 594 fake_throttle = throttling.Throttle(['fake_throttle']) 595 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 596 sync=True, execute=utils.execute, 597 ionice=None, throttle=fake_throttle) 598 self.assertIsNone(output) 599 mock_exec.assert_called_once_with('fake_throttle', 'dd', 600 'if=/dev/zero', 601 'of=/dev/null', 602 'count=%s' % units.Gi, 603 'bs=3M', 'iflag=count_bytes', 604 'conv=fdatasync', run_as_root=True) 605 606 mock_exec.reset_mock() 607 608 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 609 sync=False, execute=utils.execute, 610 ionice=None, throttle=fake_throttle) 611 self.assertIsNone(output) 612 mock_exec.assert_called_once_with('fake_throttle', 'dd', 613 'if=/dev/zero', 614 'of=/dev/null', 615 'count=%s' % units.Gi, 616 'bs=3M', 'iflag=count_bytes', 617 run_as_root=True) 618 619 @mock.patch('cinder.volume.utils.check_for_odirect_support', 620 return_value=False) 621 @mock.patch('cinder.utils.execute') 622 def test_copy_volume_dd_no_throttle(self, mock_exec, mock_support): 623 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 624 sync=True, execute=utils.execute, 625 ionice=None) 626 self.assertIsNone(output) 627 mock_exec.assert_called_once_with('dd', 'if=/dev/zero', 'of=/dev/null', 628 'count=%s' % units.Gi, 'bs=3M', 629 'iflag=count_bytes', 630 'conv=fdatasync', run_as_root=True) 631 632 @mock.patch('cinder.volume.utils.check_for_odirect_support', 633 return_value=False) 634 @mock.patch('cinder.utils.execute') 635 def test_copy_volume_dd_with_ionice(self, mock_exec, mock_support): 636 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 637 sync=True, execute=utils.execute, 638 ionice='-c3') 639 self.assertIsNone(output) 640 mock_exec.assert_called_once_with('ionice', '-c3', 'dd', 641 'if=/dev/zero', 'of=/dev/null', 642 'count=%s' % units.Gi, 'bs=3M', 643 'iflag=count_bytes', 644 'conv=fdatasync', run_as_root=True) 645 646 @mock.patch('cinder.volume.utils.check_for_odirect_support', 647 return_value=False) 648 @mock.patch('cinder.utils.execute') 649 def test_copy_volume_dd_with_sparse(self, mock_exec, mock_support): 650 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 651 sync=True, execute=utils.execute, 652 sparse=True) 653 self.assertIsNone(output) 654 mock_exec.assert_called_once_with('dd', 'if=/dev/zero', 'of=/dev/null', 655 'count=%s' % units.Gi, 'bs=3M', 656 'iflag=count_bytes', 657 'conv=fdatasync,sparse', 658 run_as_root=True) 659 660 @mock.patch('cinder.volume.utils.check_for_odirect_support', 661 return_value=True) 662 @mock.patch('cinder.utils.execute') 663 def test_copy_volume_dd_with_sparse_iflag_and_oflag(self, mock_exec, 664 mock_support): 665 output = volume_utils.copy_volume('/dev/zero', '/dev/null', 1024, '3M', 666 sync=True, execute=utils.execute, 667 sparse=True) 668 self.assertIsNone(output) 669 mock_exec.assert_called_once_with('dd', 'if=/dev/zero', 'of=/dev/null', 670 'count=%s' % units.Gi, 'bs=3M', 671 'iflag=count_bytes,direct', 672 'oflag=direct', 'conv=sparse', 673 run_as_root=True) 674 675 @mock.patch('cinder.volume.utils._copy_volume_with_file') 676 def test_copy_volume_handles(self, mock_copy): 677 handle1 = io.RawIOBase() 678 handle2 = io.RawIOBase() 679 output = volume_utils.copy_volume(handle1, handle2, 1024, 1) 680 self.assertIsNone(output) 681 mock_copy.assert_called_once_with(handle1, handle2, 1024) 682 683 @mock.patch('cinder.volume.utils._transfer_data') 684 @mock.patch('cinder.volume.utils._open_volume_with_path') 685 def test_copy_volume_handle_transfer(self, mock_open, mock_transfer): 686 handle = io.RawIOBase() 687 output = volume_utils.copy_volume('/foo/bar', handle, 1024, 1) 688 self.assertIsNone(output) 689 mock_transfer.assert_called_once_with(mock.ANY, mock.ANY, 690 1073741824, mock.ANY) 691 692 693@ddt.ddt 694class VolumeUtilsTestCase(test.TestCase): 695 def test_null_safe_str(self): 696 self.assertEqual('', volume_utils.null_safe_str(None)) 697 self.assertEqual('', volume_utils.null_safe_str(False)) 698 self.assertEqual('', volume_utils.null_safe_str(0)) 699 self.assertEqual('', volume_utils.null_safe_str([])) 700 self.assertEqual('', volume_utils.null_safe_str(())) 701 self.assertEqual('', volume_utils.null_safe_str({})) 702 self.assertEqual('', volume_utils.null_safe_str(set())) 703 self.assertEqual('a', volume_utils.null_safe_str('a')) 704 self.assertEqual('1', volume_utils.null_safe_str(1)) 705 self.assertEqual('True', volume_utils.null_safe_str(True)) 706 707 @mock.patch('cinder.utils.get_root_helper') 708 @mock.patch('cinder.brick.local_dev.lvm.LVM.supports_thin_provisioning') 709 def test_supports_thin_provisioning(self, mock_supports_thin, mock_helper): 710 self.assertEqual(mock_supports_thin.return_value, 711 volume_utils.supports_thin_provisioning()) 712 mock_helper.assert_called_once_with() 713 714 @mock.patch('cinder.utils.get_root_helper') 715 @mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_physical_volumes') 716 def test_get_all_physical_volumes(self, mock_get_vols, mock_helper): 717 self.assertEqual(mock_get_vols.return_value, 718 volume_utils.get_all_physical_volumes()) 719 mock_helper.assert_called_once_with() 720 721 @mock.patch('cinder.utils.get_root_helper') 722 @mock.patch('cinder.brick.local_dev.lvm.LVM.get_all_volume_groups') 723 def test_get_all_volume_groups(self, mock_get_groups, mock_helper): 724 self.assertEqual(mock_get_groups.return_value, 725 volume_utils.get_all_volume_groups()) 726 mock_helper.assert_called_once_with() 727 728 def test_generate_password(self): 729 password = volume_utils.generate_password() 730 self.assertTrue(any(c for c in password if c in '23456789')) 731 self.assertTrue(any(c for c in password 732 if c in 'abcdefghijkmnopqrstuvwxyz')) 733 self.assertTrue(any(c for c in password 734 if c in 'ABCDEFGHJKLMNPQRSTUVWXYZ')) 735 self.assertEqual(16, len(password)) 736 self.assertEqual(10, len(volume_utils.generate_password(10))) 737 738 @mock.patch('cinder.volume.utils.generate_password') 739 def test_generate_username(self, mock_gen_pass): 740 output = volume_utils.generate_username() 741 self.assertEqual(mock_gen_pass.return_value, output) 742 743 def test_extract_host(self): 744 host = 'Host' 745 # default level is 'backend' 746 self.assertEqual(host, 747 volume_utils.extract_host(host)) 748 self.assertEqual(host, 749 volume_utils.extract_host(host, 'host')) 750 self.assertEqual(host, 751 volume_utils.extract_host(host, 'backend')) 752 # default_pool_name doesn't work for level other than 'pool' 753 self.assertEqual(host, 754 volume_utils.extract_host(host, 'host', True)) 755 self.assertEqual(host, 756 volume_utils.extract_host(host, 'host', False)) 757 self.assertEqual(host, 758 volume_utils.extract_host(host, 'backend', True)) 759 self.assertEqual(host, 760 volume_utils.extract_host(host, 'backend', False)) 761 self.assertIsNone(volume_utils.extract_host(host, 'pool')) 762 self.assertEqual('_pool0', 763 volume_utils.extract_host(host, 'pool', True)) 764 765 host = 'Host@Backend' 766 self.assertEqual('Host@Backend', 767 volume_utils.extract_host(host)) 768 self.assertEqual('Host', 769 volume_utils.extract_host(host, 'host')) 770 self.assertEqual(host, 771 volume_utils.extract_host(host, 'backend')) 772 self.assertIsNone(volume_utils.extract_host(host, 'pool')) 773 self.assertEqual('_pool0', 774 volume_utils.extract_host(host, 'pool', True)) 775 776 host = 'Host@Backend#Pool' 777 pool = 'Pool' 778 self.assertEqual('Host@Backend', 779 volume_utils.extract_host(host)) 780 self.assertEqual('Host', 781 volume_utils.extract_host(host, 'host')) 782 self.assertEqual('Host@Backend', 783 volume_utils.extract_host(host, 'backend')) 784 self.assertEqual(pool, 785 volume_utils.extract_host(host, 'pool')) 786 self.assertEqual(pool, 787 volume_utils.extract_host(host, 'pool', True)) 788 789 host = 'Host#Pool' 790 self.assertEqual('Host', 791 volume_utils.extract_host(host)) 792 self.assertEqual('Host', 793 volume_utils.extract_host(host, 'host')) 794 self.assertEqual('Host', 795 volume_utils.extract_host(host, 'backend')) 796 self.assertEqual(pool, 797 volume_utils.extract_host(host, 'pool')) 798 self.assertEqual(pool, 799 volume_utils.extract_host(host, 'pool', True)) 800 801 def test_extract_host_none_string(self): 802 self.assertRaises(exception.InvalidVolume, 803 volume_utils.extract_host, 804 None) 805 806 def test_append_host(self): 807 host = 'Host' 808 pool = 'Pool' 809 expected = 'Host#Pool' 810 self.assertEqual(expected, 811 volume_utils.append_host(host, pool)) 812 813 pool = None 814 expected = 'Host' 815 self.assertEqual(expected, 816 volume_utils.append_host(host, pool)) 817 818 host = None 819 pool = 'pool' 820 expected = None 821 self.assertEqual(expected, 822 volume_utils.append_host(host, pool)) 823 824 host = None 825 pool = None 826 expected = None 827 self.assertEqual(expected, 828 volume_utils.append_host(host, pool)) 829 830 def test_compare_hosts(self): 831 host_1 = 'fake_host@backend1' 832 host_2 = 'fake_host@backend1#pool1' 833 self.assertTrue(volume_utils.hosts_are_equivalent(host_1, host_2)) 834 835 host_2 = 'fake_host@backend1' 836 self.assertTrue(volume_utils.hosts_are_equivalent(host_1, host_2)) 837 838 host_2 = 'fake_host2@backend1' 839 self.assertFalse(volume_utils.hosts_are_equivalent(host_1, host_2)) 840 841 @mock.patch('cinder.volume.utils.CONF') 842 def test_extract_id_from_volume_name_vol_id_pattern(self, conf_mock): 843 conf_mock.volume_name_template = 'volume-%s' 844 vol_id = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 845 vol_name = conf_mock.volume_name_template % vol_id 846 result = volume_utils.extract_id_from_volume_name(vol_name) 847 self.assertEqual(vol_id, result) 848 849 @mock.patch('cinder.volume.utils.CONF') 850 def test_extract_id_from_volume_name_vol_id_vol_pattern(self, conf_mock): 851 conf_mock.volume_name_template = 'volume-%s-volume' 852 vol_id = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 853 vol_name = conf_mock.volume_name_template % vol_id 854 result = volume_utils.extract_id_from_volume_name(vol_name) 855 self.assertEqual(vol_id, result) 856 857 @mock.patch('cinder.volume.utils.CONF') 858 def test_extract_id_from_volume_name_id_vol_pattern(self, conf_mock): 859 conf_mock.volume_name_template = '%s-volume' 860 vol_id = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 861 vol_name = conf_mock.volume_name_template % vol_id 862 result = volume_utils.extract_id_from_volume_name(vol_name) 863 self.assertEqual(vol_id, result) 864 865 @mock.patch('cinder.volume.utils.CONF') 866 def test_extract_id_from_volume_name_no_match(self, conf_mock): 867 conf_mock.volume_name_template = '%s-volume' 868 vol_name = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 869 result = volume_utils.extract_id_from_volume_name(vol_name) 870 self.assertIsNone(result) 871 vol_name = 'blahblahblah' 872 result = volume_utils.extract_id_from_volume_name(vol_name) 873 self.assertIsNone(result) 874 875 @mock.patch('cinder.db.sqlalchemy.api.resource_exists', return_value=True) 876 def test_check_managed_volume_already_managed(self, exists_mock): 877 id_ = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 878 result = volume_utils.check_already_managed_volume(id_) 879 self.assertTrue(result) 880 exists_mock.assert_called_once_with(mock.ANY, models.Volume, id_) 881 882 @mock.patch('cinder.db.sqlalchemy.api.resource_exists', return_value=False) 883 def test_check_managed_volume_not_managed_proper_uuid(self, exists_mock): 884 id_ = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 885 result = volume_utils.check_already_managed_volume(id_) 886 self.assertFalse(result) 887 exists_mock.assert_called_once_with(mock.ANY, models.Volume, id_) 888 889 def test_check_managed_volume_not_managed_invalid_id(self): 890 result = volume_utils.check_already_managed_volume(1) 891 self.assertFalse(result) 892 result = volume_utils.check_already_managed_volume('not-a-uuid') 893 self.assertFalse(result) 894 895 @mock.patch('cinder.volume.utils.CONF') 896 def test_extract_id_from_snapshot_name(self, conf_mock): 897 conf_mock.snapshot_name_template = '%s-snapshot' 898 snap_id = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 899 snap_name = conf_mock.snapshot_name_template % snap_id 900 result = volume_utils.extract_id_from_snapshot_name(snap_name) 901 self.assertEqual(snap_id, result) 902 903 @mock.patch('cinder.volume.utils.CONF') 904 def test_extract_id_from_snapshot_name_no_match(self, conf_mock): 905 conf_mock.snapshot_name_template = '%s-snapshot' 906 snap_name = 'd8cd1feb-2dcc-404d-9b15-b86fe3bec0a1' 907 result = volume_utils.extract_id_from_snapshot_name(snap_name) 908 self.assertIsNone(result) 909 snap_name = 'blahblahblah' 910 result = volume_utils.extract_id_from_snapshot_name(snap_name) 911 self.assertIsNone(result) 912 913 @ddt.data({"name": "vol02"}, '{"name": "vol02"}') 914 def test_paginate_entries_list_with_marker(self, marker): 915 entries = [{'reference': {'name': 'vol03'}, 'size': 1}, 916 {'reference': {'name': 'vol01'}, 'size': 3}, 917 {'reference': {'name': 'vol02'}, 'size': 3}, 918 {'reference': {'name': 'vol04'}, 'size': 2}, 919 {'reference': {'name': 'vol06'}, 'size': 3}, 920 {'reference': {'name': 'vol07'}, 'size': 1}, 921 {'reference': {'name': 'vol05'}, 'size': 1}] 922 expected = [{'reference': {'name': 'vol04'}, 'size': 2}, 923 {'reference': {'name': 'vol03'}, 'size': 1}, 924 {'reference': {'name': 'vol05'}, 'size': 1}] 925 res = volume_utils.paginate_entries_list(entries, marker, 3, 926 1, ['size', 'reference'], 927 ['desc', 'asc']) 928 self.assertEqual(expected, res) 929 930 def test_paginate_entries_list_without_marker(self): 931 entries = [{'reference': {'name': 'vol03'}, 'size': 1}, 932 {'reference': {'name': 'vol01'}, 'size': 3}, 933 {'reference': {'name': 'vol02'}, 'size': 3}, 934 {'reference': {'name': 'vol04'}, 'size': 2}, 935 {'reference': {'name': 'vol06'}, 'size': 3}, 936 {'reference': {'name': 'vol07'}, 'size': 1}, 937 {'reference': {'name': 'vol05'}, 'size': 1}] 938 expected = [{'reference': {'name': 'vol07'}, 'size': 1}, 939 {'reference': {'name': 'vol06'}, 'size': 3}, 940 {'reference': {'name': 'vol05'}, 'size': 1}] 941 res = volume_utils.paginate_entries_list(entries, None, 3, None, 942 ['reference'], ['desc']) 943 self.assertEqual(expected, res) 944 945 def test_paginate_entries_list_marker_invalid_format(self): 946 entries = [{'reference': {'name': 'vol03'}, 'size': 1}, 947 {'reference': {'name': 'vol01'}, 'size': 3}] 948 self.assertRaises(exception.InvalidInput, 949 volume_utils.paginate_entries_list, 950 entries, "invalid_format", 3, None, 951 ['size', 'reference'], ['desc', 'asc']) 952 953 def test_paginate_entries_list_marker_not_found(self): 954 entries = [{'reference': {'name': 'vol03'}, 'size': 1}, 955 {'reference': {'name': 'vol01'}, 'size': 3}] 956 self.assertRaises(exception.InvalidInput, 957 volume_utils.paginate_entries_list, 958 entries, {'name': 'vol02'}, 3, None, 959 ['size', 'reference'], ['desc', 'asc']) 960 961 def test_convert_config_string_to_dict(self): 962 test_string = "{'key-1'='val-1' 'key-2'='val-2' 'key-3'='val-3'}" 963 expected_dict = {'key-1': 'val-1', 'key-2': 'val-2', 'key-3': 'val-3'} 964 965 self.assertEqual( 966 expected_dict, 967 volume_utils.convert_config_string_to_dict(test_string)) 968 969 @mock.patch('cinder.volume.volume_types.is_encrypted', return_value=False) 970 def test_create_encryption_key_unencrypted(self, is_encrypted): 971 result = volume_utils.create_encryption_key(mock.ANY, 972 mock.ANY, 973 fake.VOLUME_TYPE_ID) 974 self.assertIsNone(result) 975 976 @mock.patch('cinder.volume.volume_types.is_encrypted', return_value=True) 977 @mock.patch('cinder.volume.volume_types.get_volume_type_encryption') 978 @mock.patch('cinder.keymgr.conf_key_mgr.ConfKeyManager.create_key') 979 def test_create_encryption_key_encrypted(self, create_key, 980 get_volume_type_encryption, 981 is_encryption): 982 enc_key = {'cipher': 'aes-xts-plain64', 983 'key_size': 256, 984 'provider': 'p1', 985 'control_location': 'front-end', 986 'encryption_id': 'uuid1'} 987 ctxt = context.get_admin_context() 988 type_ref1 = volume_types.create(ctxt, "type1") 989 encryption = db.volume_type_encryption_create( 990 ctxt, type_ref1['id'], enc_key) 991 get_volume_type_encryption.return_value = encryption 992 CONF.set_override( 993 'backend', 994 'cinder.keymgr.conf_key_mgr.ConfKeyManager', 995 group='key_manager') 996 km = key_manager.API() 997 volume_utils.create_encryption_key(ctxt, 998 km, 999 fake.VOLUME_TYPE_ID) 1000 is_encryption.assert_called_once_with(ctxt, 1001 fake.VOLUME_TYPE_ID) 1002 get_volume_type_encryption.assert_called_once_with( 1003 ctxt, 1004 fake.VOLUME_TYPE_ID) 1005 create_key.assert_called_once_with(ctxt, 1006 algorithm='aes', 1007 length=256) 1008 1009 @ddt.data('<is> True', '<is> true', '<is> yes') 1010 def test_is_replicated_spec_true(self, enabled): 1011 res = volume_utils.is_replicated_spec({'replication_enabled': enabled}) 1012 self.assertTrue(res) 1013 1014 @ddt.data({}, None, {'key': 'value'}) 1015 def test_is_replicated_no_specs(self, extra_specs): 1016 res = volume_utils.is_replicated_spec(extra_specs) 1017 self.assertFalse(bool(res)) 1018 1019 @ddt.data('<is> False', '<is> false', '<is> f', 'baddata', 'bad data') 1020 def test_is_replicated_spec_false(self, enabled): 1021 res = volume_utils.is_replicated_spec({'replication_enabled': enabled}) 1022 self.assertFalse(res) 1023 1024 @mock.patch('cinder.db.group_get') 1025 def test_group_get_by_id(self, mock_db_group_get): 1026 expected = mock.Mock() 1027 mock_db_group_get.return_value = expected 1028 group_id = fake.GROUP_ID 1029 actual = volume_utils.group_get_by_id(group_id) 1030 self.assertEqual(expected, actual) 1031 1032 @mock.patch('cinder.db.group_get') 1033 def test_group_get_by_id_group_not_found(self, mock_db_group_get): 1034 group_id = fake.GROUP_ID 1035 mock_db_group_get.side_effect = exception.GroupNotFound( 1036 group_id=group_id) 1037 self.assertRaises( 1038 exception.GroupNotFound, 1039 volume_utils.group_get_by_id, 1040 group_id 1041 ) 1042 1043 @ddt.data('<is> False', None, 'notASpecValueWeCareAbout') 1044 def test_is_group_a_cg_snapshot_type_is_false(self, spec_value): 1045 with mock.patch('cinder.volume.group_types' 1046 '.get_group_type_specs') as mock_get_specs: 1047 mock_get_specs.return_value = spec_value 1048 group = fake_group.fake_group_obj( 1049 None, group_type_id=fake.GROUP_TYPE_ID) 1050 self.assertFalse(volume_utils.is_group_a_cg_snapshot_type(group)) 1051 1052 @mock.patch('cinder.volume.group_types.get_group_type_specs') 1053 def test_is_group_a_cg_snapshot_type_is_true(self, mock_get_specs): 1054 mock_get_specs.return_value = '<is> True' 1055 group = fake_group.fake_group_obj( 1056 None, group_type_id=fake.GROUP_TYPE_ID) 1057 self.assertTrue(volume_utils.is_group_a_cg_snapshot_type(group)) 1058 1059 @ddt.data({'max_over_subscription_ratio': '10', 'supports_auto': True}, 1060 {'max_over_subscription_ratio': 'auto', 'supports_auto': True}, 1061 {'max_over_subscription_ratio': 'auto', 'supports_auto': False}, 1062 {'max_over_subscription_ratio': '1.2', 'supports_auto': False},) 1063 @ddt.unpack 1064 def test_get_max_over_subscription_ratio(self, 1065 max_over_subscription_ratio, 1066 supports_auto): 1067 1068 if not supports_auto and max_over_subscription_ratio == 'auto': 1069 self.assertRaises(exception.VolumeDriverException, 1070 volume_utils.get_max_over_subscription_ratio, 1071 max_over_subscription_ratio, supports_auto) 1072 elif not supports_auto: 1073 mosr = volume_utils.get_max_over_subscription_ratio( 1074 max_over_subscription_ratio, supports_auto) 1075 self.assertEqual(float(max_over_subscription_ratio), mosr) 1076 else: # supports_auto 1077 mosr = volume_utils.get_max_over_subscription_ratio( 1078 max_over_subscription_ratio, supports_auto) 1079 if max_over_subscription_ratio == 'auto': 1080 self.assertEqual(max_over_subscription_ratio, mosr) 1081 else: 1082 self.assertEqual(float(max_over_subscription_ratio), mosr) 1083