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 datetime 14import iso8601 15import sys 16import time 17 18import ddt 19import fixtures 20import mock 21from oslo_config import cfg 22from oslo_db import exception as oslo_exception 23from oslo_utils import timeutils 24import six 25from six.moves import StringIO 26 27try: 28 import rtslib_fb 29except ImportError: 30 import rtslib as rtslib_fb 31 32 33from cinder.cmd import api as cinder_api 34from cinder.cmd import backup as cinder_backup 35from cinder.cmd import manage as cinder_manage 36from cinder.cmd import rtstool as cinder_rtstool 37from cinder.cmd import scheduler as cinder_scheduler 38from cinder.cmd import volume as cinder_volume 39from cinder.cmd import volume_usage_audit 40from cinder.common import constants 41from cinder import context 42from cinder.db.sqlalchemy import api as sqlalchemy_api 43from cinder import exception 44from cinder.objects import fields 45from cinder import test 46from cinder.tests.unit import fake_cluster 47from cinder.tests.unit import fake_constants as fake 48from cinder.tests.unit import fake_service 49from cinder.tests.unit import fake_volume 50from cinder.tests.unit import utils 51from cinder import version 52from cinder.volume import rpcapi 53 54CONF = cfg.CONF 55 56 57class TestCinderApiCmd(test.TestCase): 58 """Unit test cases for python modules under cinder/cmd.""" 59 60 def setUp(self): 61 super(TestCinderApiCmd, self).setUp() 62 sys.argv = ['cinder-api'] 63 64 @mock.patch('cinder.service.WSGIService') 65 @mock.patch('cinder.service.process_launcher') 66 @mock.patch('cinder.rpc.init') 67 @mock.patch('cinder.utils.monkey_patch') 68 @mock.patch('oslo_log.log.setup') 69 def test_main(self, log_setup, monkey_patch, rpc_init, process_launcher, 70 wsgi_service): 71 launcher = process_launcher.return_value 72 server = wsgi_service.return_value 73 server.workers = mock.sentinel.worker_count 74 75 cinder_api.main() 76 77 self.assertEqual('cinder', CONF.project) 78 self.assertEqual(CONF.version, version.version_string()) 79 log_setup.assert_called_once_with(CONF, "cinder") 80 monkey_patch.assert_called_once_with() 81 rpc_init.assert_called_once_with(CONF) 82 process_launcher.assert_called_once_with() 83 wsgi_service.assert_called_once_with('osapi_volume') 84 launcher.launch_service.assert_called_once_with( 85 server, 86 workers=server.workers) 87 launcher.wait.assert_called_once_with() 88 89 90class TestCinderBackupCmd(test.TestCase): 91 92 def setUp(self): 93 super(TestCinderBackupCmd, self).setUp() 94 sys.argv = ['cinder-backup'] 95 96 @mock.patch('cinder.service.wait') 97 @mock.patch('cinder.service.serve') 98 @mock.patch('cinder.service.Service.create') 99 @mock.patch('cinder.utils.monkey_patch') 100 @mock.patch('oslo_log.log.setup') 101 def test_main(self, log_setup, monkey_patch, service_create, service_serve, 102 service_wait): 103 server = service_create.return_value 104 105 cinder_backup.main() 106 107 self.assertEqual('cinder', CONF.project) 108 self.assertEqual(CONF.version, version.version_string()) 109 log_setup.assert_called_once_with(CONF, "cinder") 110 monkey_patch.assert_called_once_with() 111 service_create.assert_called_once_with(binary='cinder-backup', 112 coordination=True) 113 service_serve.assert_called_once_with(server) 114 service_wait.assert_called_once_with() 115 116 117class TestCinderSchedulerCmd(test.TestCase): 118 119 def setUp(self): 120 super(TestCinderSchedulerCmd, self).setUp() 121 sys.argv = ['cinder-scheduler'] 122 123 @mock.patch('cinder.service.wait') 124 @mock.patch('cinder.service.serve') 125 @mock.patch('cinder.service.Service.create') 126 @mock.patch('cinder.utils.monkey_patch') 127 @mock.patch('oslo_log.log.setup') 128 def test_main(self, log_setup, monkey_patch, service_create, 129 service_serve, service_wait): 130 server = service_create.return_value 131 132 cinder_scheduler.main() 133 134 self.assertEqual('cinder', CONF.project) 135 self.assertEqual(CONF.version, version.version_string()) 136 log_setup.assert_called_once_with(CONF, "cinder") 137 monkey_patch.assert_called_once_with() 138 service_create.assert_called_once_with(binary='cinder-scheduler') 139 service_serve.assert_called_once_with(server) 140 service_wait.assert_called_once_with() 141 142 143class TestCinderVolumeCmdPosix(test.TestCase): 144 145 def setUp(self): 146 super(TestCinderVolumeCmdPosix, self).setUp() 147 sys.argv = ['cinder-volume'] 148 149 self.patch('os.name', 'posix') 150 151 @mock.patch('cinder.service.get_launcher') 152 @mock.patch('cinder.service.Service.create') 153 @mock.patch('cinder.utils.monkey_patch') 154 @mock.patch('oslo_log.log.setup') 155 def test_main(self, log_setup, monkey_patch, service_create, 156 get_launcher): 157 CONF.set_override('enabled_backends', None) 158 self.assertRaises(SystemExit, cinder_volume.main) 159 self.assertFalse(service_create.called) 160 161 @mock.patch('cinder.service.get_launcher') 162 @mock.patch('cinder.service.Service.create') 163 @mock.patch('cinder.utils.monkey_patch') 164 @mock.patch('oslo_log.log.setup') 165 def test_main_with_backends(self, log_setup, monkey_patch, service_create, 166 get_launcher): 167 backends = ['', 'backend1', 'backend2', ''] 168 CONF.set_override('enabled_backends', backends) 169 CONF.set_override('host', 'host') 170 launcher = get_launcher.return_value 171 172 cinder_volume.main() 173 174 self.assertEqual('cinder', CONF.project) 175 self.assertEqual(CONF.version, version.version_string()) 176 log_setup.assert_called_once_with(CONF, "cinder") 177 monkey_patch.assert_called_once_with() 178 get_launcher.assert_called_once_with() 179 c1 = mock.call(binary=constants.VOLUME_BINARY, host='host@backend1', 180 service_name='backend1', coordination=True, 181 cluster=None) 182 c2 = mock.call(binary=constants.VOLUME_BINARY, host='host@backend2', 183 service_name='backend2', coordination=True, 184 cluster=None) 185 service_create.assert_has_calls([c1, c2]) 186 self.assertEqual(2, launcher.launch_service.call_count) 187 launcher.wait.assert_called_once_with() 188 189 190@ddt.ddt 191class TestCinderVolumeCmdWin32(test.TestCase): 192 193 def setUp(self): 194 super(TestCinderVolumeCmdWin32, self).setUp() 195 sys.argv = ['cinder-volume'] 196 197 self._mock_win32_proc_launcher = mock.Mock() 198 199 self.patch('os.name', 'nt') 200 self.patch('cinder.service.WindowsProcessLauncher', 201 lambda *args, **kwargs: self._mock_win32_proc_launcher) 202 203 @mock.patch('cinder.service.get_launcher') 204 @mock.patch('cinder.service.Service.create') 205 @mock.patch('cinder.utils.monkey_patch') 206 @mock.patch('oslo_log.log.setup') 207 def test_main(self, log_setup, monkey_patch, service_create, 208 get_launcher): 209 CONF.set_override('enabled_backends', None) 210 self.assertRaises(SystemExit, cinder_volume.main) 211 self.assertFalse(service_create.called) 212 self.assertFalse(self._mock_win32_proc_launcher.called) 213 214 @mock.patch('cinder.service.get_launcher') 215 @mock.patch('cinder.service.Service.create') 216 @mock.patch('cinder.utils.monkey_patch') 217 @mock.patch('oslo_log.log.setup') 218 def test_main_invalid_backend(self, log_setup, monkey_patch, 219 service_create, get_launcher): 220 CONF.set_override('enabled_backends', 'backend1') 221 CONF.set_override('backend_name', 'backend2') 222 self.assertRaises(exception.InvalidInput, cinder_volume.main) 223 self.assertFalse(service_create.called) 224 self.assertFalse(self._mock_win32_proc_launcher.called) 225 226 @mock.patch('cinder.utils.monkey_patch') 227 @mock.patch('oslo_log.log.setup') 228 @ddt.data({}, 229 {'binary_path': 'cinder-volume-script.py', 230 'exp_py_executable': True}) 231 @ddt.unpack 232 def test_main_with_multiple_backends(self, log_setup, monkey_patch, 233 binary_path='cinder-volume', 234 exp_py_executable=False): 235 # If multiple backends are used, we expect the Windows process 236 # launcher to be used in order to create the child processes. 237 backends = ['', 'backend1', 'backend2', ''] 238 CONF.set_override('enabled_backends', backends) 239 CONF.set_override('host', 'host') 240 launcher = self._mock_win32_proc_launcher 241 242 # Depending on the setuptools version, '-script.py' and '.exe' 243 # binary path extensions may be trimmed. We need to take this 244 # into consideration when building the command that will be 245 # used to spawn child subprocesses. 246 sys.argv = [binary_path] 247 248 cinder_volume.main() 249 250 self.assertEqual('cinder', CONF.project) 251 self.assertEqual(CONF.version, version.version_string()) 252 log_setup.assert_called_once_with(CONF, "cinder") 253 monkey_patch.assert_called_once_with() 254 255 exp_cmd_prefix = [sys.executable] if exp_py_executable else [] 256 exp_cmds = [ 257 exp_cmd_prefix + sys.argv + ['--backend_name=%s' % backend_name] 258 for backend_name in ['backend1', 'backend2']] 259 launcher.add_process.assert_has_calls( 260 [mock.call(exp_cmd) for exp_cmd in exp_cmds]) 261 launcher.wait.assert_called_once_with() 262 263 @mock.patch('cinder.service.get_launcher') 264 @mock.patch('cinder.service.Service.create') 265 @mock.patch('cinder.utils.monkey_patch') 266 @mock.patch('oslo_log.log.setup') 267 def test_main_with_multiple_backends_child( 268 self, log_setup, monkey_patch, service_create, get_launcher): 269 # We're testing the code expected to be run within child processes. 270 backends = ['', 'backend1', 'backend2', ''] 271 CONF.set_override('enabled_backends', backends) 272 CONF.set_override('host', 'host') 273 launcher = get_launcher.return_value 274 275 sys.argv += ['--backend_name', 'backend2'] 276 277 cinder_volume.main() 278 279 self.assertEqual('cinder', CONF.project) 280 self.assertEqual(CONF.version, version.version_string()) 281 log_setup.assert_called_once_with(CONF, "cinder") 282 monkey_patch.assert_called_once_with() 283 284 service_create.assert_called_once_with( 285 binary=constants.VOLUME_BINARY, host='host@backend2', 286 service_name='backend2', coordination=True, 287 cluster=None) 288 launcher.launch_service.assert_called_once_with( 289 service_create.return_value) 290 291 @mock.patch('cinder.service.get_launcher') 292 @mock.patch('cinder.service.Service.create') 293 @mock.patch('cinder.utils.monkey_patch') 294 @mock.patch('oslo_log.log.setup') 295 def test_main_with_single_backend( 296 self, log_setup, monkey_patch, service_create, get_launcher): 297 # We're expecting the service to be run within the same process. 298 CONF.set_override('enabled_backends', ['backend2']) 299 CONF.set_override('host', 'host') 300 launcher = get_launcher.return_value 301 302 cinder_volume.main() 303 304 self.assertEqual('cinder', CONF.project) 305 self.assertEqual(CONF.version, version.version_string()) 306 log_setup.assert_called_once_with(CONF, "cinder") 307 monkey_patch.assert_called_once_with() 308 309 service_create.assert_called_once_with( 310 binary=constants.VOLUME_BINARY, host='host@backend2', 311 service_name='backend2', coordination=True, 312 cluster=None) 313 launcher.launch_service.assert_called_once_with( 314 service_create.return_value) 315 316 317@ddt.ddt 318class TestCinderManageCmd(test.TestCase): 319 320 def setUp(self): 321 super(TestCinderManageCmd, self).setUp() 322 sys.argv = ['cinder-manage'] 323 324 def _test_purge_invalid_age_in_days(self, age_in_days): 325 db_cmds = cinder_manage.DbCommands() 326 ex = self.assertRaises(SystemExit, db_cmds.purge, age_in_days) 327 self.assertEqual(1, ex.code) 328 329 @mock.patch('cinder.objects.ServiceList.get_all') 330 @mock.patch('cinder.db.migration.db_sync') 331 def test_db_commands_sync(self, db_sync, service_get_mock): 332 version = 11 333 db_cmds = cinder_manage.DbCommands() 334 db_cmds.sync(version=version) 335 db_sync.assert_called_once_with(version) 336 service_get_mock.assert_not_called() 337 338 @mock.patch('cinder.objects.Service.save') 339 @mock.patch('cinder.objects.ServiceList.get_all') 340 @mock.patch('cinder.db.migration.db_sync') 341 def test_db_commands_sync_bump_versions(self, db_sync, service_get_mock, 342 service_save): 343 ctxt = context.get_admin_context() 344 services = [fake_service.fake_service_obj(ctxt, 345 binary='cinder-' + binary, 346 rpc_current_version='0.1', 347 object_current_version='0.2') 348 for binary in ('volume', 'scheduler', 'backup')] 349 service_get_mock.return_value = services 350 351 version = 11 352 db_cmds = cinder_manage.DbCommands() 353 db_cmds.sync(version=version, bump_versions=True) 354 db_sync.assert_called_once_with(version) 355 356 self.assertEqual(3, service_save.call_count) 357 for service in services: 358 self.assertEqual(cinder_manage.RPC_VERSIONS[service.binary], 359 service.rpc_current_version) 360 self.assertEqual(cinder_manage.OVO_VERSION, 361 service.object_current_version) 362 363 @mock.patch('oslo_db.sqlalchemy.migration.db_version') 364 def test_db_commands_version(self, db_version): 365 db_cmds = cinder_manage.DbCommands() 366 with mock.patch('sys.stdout', new=six.StringIO()): 367 db_cmds.version() 368 self.assertEqual(1, db_version.call_count) 369 370 def test_db_commands_upgrade_out_of_range(self): 371 version = 2147483647 372 db_cmds = cinder_manage.DbCommands() 373 exit = self.assertRaises(SystemExit, db_cmds.sync, version + 1) 374 self.assertEqual(1, exit.code) 375 376 @mock.patch("oslo_db.sqlalchemy.migration.db_sync") 377 def test_db_commands_script_not_present(self, db_sync): 378 db_sync.side_effect = oslo_exception.DBMigrationError(None) 379 db_cmds = cinder_manage.DbCommands() 380 exit = self.assertRaises(SystemExit, db_cmds.sync, 101) 381 self.assertEqual(1, exit.code) 382 383 @mock.patch('cinder.cmd.manage.DbCommands.online_migrations', 384 (mock.Mock(side_effect=((2, 2), (0, 0)), __name__='foo'),)) 385 def test_db_commands_online_data_migrations(self): 386 db_cmds = cinder_manage.DbCommands() 387 exit = self.assertRaises(SystemExit, db_cmds.online_data_migrations) 388 self.assertEqual(0, exit.code) 389 cinder_manage.DbCommands.online_migrations[0].assert_has_calls( 390 (mock.call(mock.ANY, 50),) * 2) 391 392 def _fake_db_command(self, migrations=None): 393 if migrations is None: 394 mock_mig_1 = mock.MagicMock(__name__="mock_mig_1") 395 mock_mig_2 = mock.MagicMock(__name__="mock_mig_2") 396 mock_mig_1.return_value = (5, 4) 397 mock_mig_2.return_value = (6, 6) 398 migrations = (mock_mig_1, mock_mig_2) 399 400 class _CommandSub(cinder_manage.DbCommands): 401 online_migrations = migrations 402 403 return _CommandSub 404 405 @mock.patch('cinder.context.get_admin_context') 406 def test_online_migrations(self, mock_get_context): 407 self.useFixture(fixtures.MonkeyPatch('sys.stdout', StringIO())) 408 ctxt = mock_get_context.return_value 409 db_cmds = self._fake_db_command() 410 command = db_cmds() 411 exit = self.assertRaises(SystemExit, 412 command.online_data_migrations, 10) 413 self.assertEqual(1, exit.code) 414 expected = """\ 4155 rows matched query mock_mig_1, 4 migrated, 1 remaining 4166 rows matched query mock_mig_2, 6 migrated, 0 remaining 417+------------+--------------+-----------+ 418| Migration | Total Needed | Completed | 419+------------+--------------+-----------+ 420| mock_mig_1 | 5 | 4 | 421| mock_mig_2 | 6 | 6 | 422+------------+--------------+-----------+ 423""" 424 command.online_migrations[0].assert_has_calls([mock.call(ctxt, 425 10)]) 426 command.online_migrations[1].assert_has_calls([mock.call(ctxt, 427 6)]) 428 429 self.assertEqual(expected, sys.stdout.getvalue()) 430 431 @mock.patch('cinder.cmd.manage.DbCommands.online_migrations', 432 (mock.Mock(side_effect=((2, 2), (0, 0)), __name__='foo'),)) 433 def test_db_commands_online_data_migrations_ignore_state_and_max(self): 434 db_cmds = cinder_manage.DbCommands() 435 exit = self.assertRaises(SystemExit, db_cmds.online_data_migrations, 436 2) 437 self.assertEqual(1, exit.code) 438 cinder_manage.DbCommands.online_migrations[0].assert_called_once_with( 439 mock.ANY, 2) 440 441 @mock.patch('cinder.cmd.manage.DbCommands.online_migrations', 442 (mock.Mock(side_effect=((2, 2), (0, 0)), __name__='foo'),)) 443 def test_db_commands_online_data_migrations_max_negative(self): 444 db_cmds = cinder_manage.DbCommands() 445 exit = self.assertRaises(SystemExit, db_cmds.online_data_migrations, 446 -1) 447 self.assertEqual(127, exit.code) 448 cinder_manage.DbCommands.online_migrations[0].assert_not_called() 449 450 @mock.patch('cinder.version.version_string') 451 def test_versions_commands_list(self, version_string): 452 version_cmds = cinder_manage.VersionCommands() 453 with mock.patch('sys.stdout', new=six.StringIO()): 454 version_cmds.list() 455 version_string.assert_called_once_with() 456 457 @mock.patch('cinder.version.version_string') 458 def test_versions_commands_call(self, version_string): 459 version_cmds = cinder_manage.VersionCommands() 460 with mock.patch('sys.stdout', new=six.StringIO()): 461 version_cmds.__call__() 462 version_string.assert_called_once_with() 463 464 def test_purge_with_negative_age_in_days(self): 465 age_in_days = -1 466 self._test_purge_invalid_age_in_days(age_in_days) 467 468 def test_purge_exceeded_age_in_days_limit(self): 469 age_in_days = int(time.time() / 86400) + 1 470 self._test_purge_invalid_age_in_days(age_in_days) 471 472 @mock.patch('cinder.db.sqlalchemy.api.purge_deleted_rows') 473 @mock.patch('cinder.context.get_admin_context') 474 def test_purge_less_than_age_in_days_limit(self, get_admin_context, 475 purge_deleted_rows): 476 age_in_days = int(time.time() / 86400) - 1 477 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, 478 is_admin=True) 479 get_admin_context.return_value = ctxt 480 481 purge_deleted_rows.return_value = None 482 483 db_cmds = cinder_manage.DbCommands() 484 db_cmds.purge(age_in_days) 485 486 get_admin_context.assert_called_once_with() 487 purge_deleted_rows.assert_called_once_with( 488 ctxt, age_in_days=age_in_days) 489 490 @mock.patch('cinder.db.service_get_all') 491 @mock.patch('cinder.context.get_admin_context') 492 def test_host_commands_list(self, get_admin_context, service_get_all): 493 get_admin_context.return_value = mock.sentinel.ctxt 494 service_get_all.return_value = [ 495 {'host': 'fake-host', 496 'availability_zone': 'fake-az', 497 'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}] 498 499 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 500 expected_out = ("%(host)-25s\t%(zone)-15s\n" % 501 {'host': 'host', 'zone': 'zone'}) 502 expected_out += ("%(host)-25s\t%(availability_zone)-15s\n" % 503 {'host': 'fake-host', 504 'availability_zone': 'fake-az'}) 505 host_cmds = cinder_manage.HostCommands() 506 host_cmds.list() 507 508 get_admin_context.assert_called_once_with() 509 service_get_all.assert_called_once_with(mock.sentinel.ctxt) 510 self.assertEqual(expected_out, fake_out.getvalue()) 511 512 @mock.patch('cinder.db.service_get_all') 513 @mock.patch('cinder.context.get_admin_context') 514 def test_host_commands_list_with_zone(self, get_admin_context, 515 service_get_all): 516 get_admin_context.return_value = mock.sentinel.ctxt 517 service_get_all.return_value = [ 518 {'host': 'fake-host', 519 'availability_zone': 'fake-az1', 520 'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}, 521 {'host': 'fake-host', 522 'availability_zone': 'fake-az2', 523 'uuid': '4200b32b-0bf9-436c-86b2-0675f6ac218e'}] 524 525 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 526 expected_out = ("%(host)-25s\t%(zone)-15s\n" % 527 {'host': 'host', 'zone': 'zone'}) 528 expected_out += ("%(host)-25s\t%(availability_zone)-15s\n" % 529 {'host': 'fake-host', 530 'availability_zone': 'fake-az1'}) 531 host_cmds = cinder_manage.HostCommands() 532 host_cmds.list(zone='fake-az1') 533 534 get_admin_context.assert_called_once_with() 535 service_get_all.assert_called_once_with(mock.sentinel.ctxt) 536 self.assertEqual(expected_out, fake_out.getvalue()) 537 538 @mock.patch('cinder.db.sqlalchemy.api.volume_get') 539 @mock.patch('cinder.context.get_admin_context') 540 @mock.patch('cinder.rpc.get_client') 541 @mock.patch('cinder.rpc.init') 542 def test_volume_commands_delete(self, rpc_init, get_client, 543 get_admin_context, volume_get): 544 ctxt = context.RequestContext('admin', 'fake', True) 545 get_admin_context.return_value = ctxt 546 mock_client = mock.MagicMock() 547 cctxt = mock.MagicMock() 548 mock_client.prepare.return_value = cctxt 549 get_client.return_value = mock_client 550 host = 'fake@host' 551 db_volume = {'host': host + '#pool1'} 552 volume = fake_volume.fake_db_volume(**db_volume) 553 volume_obj = fake_volume.fake_volume_obj(ctxt, **volume) 554 volume_id = volume['id'] 555 volume_get.return_value = volume 556 557 volume_cmds = cinder_manage.VolumeCommands() 558 volume_cmds._client = mock_client 559 volume_cmds.delete(volume_id) 560 561 volume_get.assert_called_once_with(ctxt, volume_id) 562 mock_client.prepare.assert_called_once_with( 563 server="fake", 564 topic="cinder-volume.fake@host", 565 version="3.0") 566 567 cctxt.cast.assert_called_once_with( 568 ctxt, 'delete_volume', 569 cascade=False, 570 unmanage_only=False, 571 volume=volume_obj) 572 573 @mock.patch('cinder.db.volume_destroy') 574 @mock.patch('cinder.db.sqlalchemy.api.volume_get') 575 @mock.patch('cinder.context.get_admin_context') 576 @mock.patch('cinder.rpc.init') 577 def test_volume_commands_delete_no_host(self, rpc_init, get_admin_context, 578 volume_get, volume_destroy): 579 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, 580 is_admin=True) 581 get_admin_context.return_value = ctxt 582 volume = fake_volume.fake_db_volume() 583 volume_id = volume['id'] 584 volume_get.return_value = volume 585 586 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 587 expected_out = ('Volume not yet assigned to host.\n' 588 'Deleting volume from database and skipping' 589 ' rpc.\n') 590 volume_cmds = cinder_manage.VolumeCommands() 591 volume_cmds.delete(volume_id) 592 593 get_admin_context.assert_called_once_with() 594 volume_get.assert_called_once_with(ctxt, volume_id) 595 self.assertTrue(volume_destroy.called) 596 admin_context = volume_destroy.call_args[0][0] 597 self.assertTrue(admin_context.is_admin) 598 self.assertEqual(expected_out, fake_out.getvalue()) 599 600 @mock.patch('cinder.db.volume_destroy') 601 @mock.patch('cinder.db.sqlalchemy.api.volume_get') 602 @mock.patch('cinder.context.get_admin_context') 603 @mock.patch('cinder.rpc.init') 604 def test_volume_commands_delete_volume_in_use(self, rpc_init, 605 get_admin_context, 606 volume_get, volume_destroy): 607 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 608 get_admin_context.return_value = ctxt 609 db_volume = {'status': 'in-use', 'host': 'fake-host'} 610 volume = fake_volume.fake_db_volume(**db_volume) 611 volume_id = volume['id'] 612 volume_get.return_value = volume 613 614 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 615 expected_out = ('Volume is in-use.\n' 616 'Detach volume from instance and then try' 617 ' again.\n') 618 volume_cmds = cinder_manage.VolumeCommands() 619 volume_cmds.delete(volume_id) 620 621 volume_get.assert_called_once_with(ctxt, volume_id) 622 self.assertEqual(expected_out, fake_out.getvalue()) 623 624 def test_config_commands_list(self): 625 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 626 expected_out = '' 627 for key, value in CONF.items(): 628 expected_out += '%s = %s' % (key, value) + '\n' 629 630 config_cmds = cinder_manage.ConfigCommands() 631 config_cmds.list() 632 633 self.assertEqual(expected_out, fake_out.getvalue()) 634 635 def test_config_commands_list_param(self): 636 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 637 CONF.set_override('host', 'fake') 638 expected_out = 'host = fake\n' 639 640 config_cmds = cinder_manage.ConfigCommands() 641 config_cmds.list(param='host') 642 643 self.assertEqual(expected_out, fake_out.getvalue()) 644 645 def test_get_log_commands_no_errors(self): 646 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 647 CONF.set_override('log_dir', None) 648 expected_out = 'No errors in logfiles!\n' 649 650 get_log_cmds = cinder_manage.GetLogCommands() 651 get_log_cmds.errors() 652 653 out_lines = fake_out.getvalue().splitlines(True) 654 655 self.assertTrue(out_lines[0].startswith('DEPRECATED')) 656 self.assertEqual(expected_out, out_lines[1]) 657 658 @mock.patch('six.moves.builtins.open') 659 @mock.patch('os.listdir') 660 def test_get_log_commands_errors(self, listdir, open): 661 CONF.set_override('log_dir', 'fake-dir') 662 listdir.return_value = ['fake-error.log'] 663 664 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 665 open.return_value = six.StringIO( 666 '[ ERROR ] fake-error-message') 667 expected_out = ['fake-dir/fake-error.log:-\n', 668 'Line 1 : [ ERROR ] fake-error-message\n'] 669 670 get_log_cmds = cinder_manage.GetLogCommands() 671 get_log_cmds.errors() 672 673 out_lines = fake_out.getvalue().splitlines(True) 674 675 self.assertTrue(out_lines[0].startswith('DEPRECATED')) 676 self.assertEqual(expected_out[0], out_lines[1]) 677 self.assertEqual(expected_out[1], out_lines[2]) 678 679 open.assert_called_once_with('fake-dir/fake-error.log', 'r') 680 listdir.assert_called_once_with(CONF.log_dir) 681 682 @mock.patch('six.moves.builtins.open') 683 @mock.patch('os.path.exists') 684 def test_get_log_commands_syslog_no_log_file(self, path_exists, open): 685 path_exists.return_value = False 686 687 get_log_cmds = cinder_manage.GetLogCommands() 688 with mock.patch('sys.stdout', new=six.StringIO()): 689 exit = self.assertRaises(SystemExit, get_log_cmds.syslog) 690 self.assertEqual(1, exit.code) 691 692 path_exists.assert_any_call('/var/log/syslog') 693 path_exists.assert_any_call('/var/log/messages') 694 695 @mock.patch('cinder.db.backup_get_all') 696 @mock.patch('cinder.context.get_admin_context') 697 def test_backup_commands_list(self, get_admin_context, backup_get_all): 698 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 699 get_admin_context.return_value = ctxt 700 backup = {'id': fake.BACKUP_ID, 701 'user_id': fake.USER_ID, 702 'project_id': fake.PROJECT_ID, 703 'host': 'fake-host', 704 'display_name': 'fake-display-name', 705 'container': 'fake-container', 706 'status': fields.BackupStatus.AVAILABLE, 707 'size': 123, 708 'object_count': 1, 709 'volume_id': fake.VOLUME_ID, 710 'backup_metadata': {}, 711 } 712 backup_get_all.return_value = [backup] 713 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 714 hdr = ('%-32s\t%-32s\t%-32s\t%-24s\t%-24s\t%-12s\t%-12s\t%-12s' 715 '\t%-12s') 716 header = hdr % ('ID', 717 'User ID', 718 'Project ID', 719 'Host', 720 'Name', 721 'Container', 722 'Status', 723 'Size', 724 'Object Count') 725 res = ('%-32s\t%-32s\t%-32s\t%-24s\t%-24s\t%-12s\t%-12s\t%-12d' 726 '\t%-12s') 727 resource = res % (backup['id'], 728 backup['user_id'], 729 backup['project_id'], 730 backup['host'], 731 backup['display_name'], 732 backup['container'], 733 backup['status'], 734 backup['size'], 735 1) 736 expected_out = header + '\n' + resource + '\n' 737 738 backup_cmds = cinder_manage.BackupCommands() 739 backup_cmds.list() 740 741 get_admin_context.assert_called_once_with() 742 backup_get_all.assert_called_once_with(ctxt, None, None, None, 743 None, None, None) 744 self.assertEqual(expected_out, fake_out.getvalue()) 745 746 @mock.patch('cinder.db.backup_update') 747 @mock.patch('cinder.db.backup_get_all_by_host') 748 @mock.patch('cinder.context.get_admin_context') 749 def test_update_backup_host(self, get_admin_context, 750 backup_get_by_host, 751 backup_update): 752 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 753 get_admin_context.return_value = ctxt 754 backup = {'id': fake.BACKUP_ID, 755 'user_id': fake.USER_ID, 756 'project_id': fake.PROJECT_ID, 757 'host': 'fake-host', 758 'display_name': 'fake-display-name', 759 'container': 'fake-container', 760 'status': fields.BackupStatus.AVAILABLE, 761 'size': 123, 762 'object_count': 1, 763 'volume_id': fake.VOLUME_ID, 764 'backup_metadata': {}, 765 } 766 backup_get_by_host.return_value = [backup] 767 backup_cmds = cinder_manage.BackupCommands() 768 backup_cmds.update_backup_host('fake_host', 'fake_host2') 769 770 get_admin_context.assert_called_once_with() 771 backup_get_by_host.assert_called_once_with(ctxt, 'fake_host') 772 backup_update.assert_called_once_with(ctxt, fake.BACKUP_ID, 773 {'host': 'fake_host2'}) 774 775 @mock.patch('cinder.db.consistencygroup_update') 776 @mock.patch('cinder.db.consistencygroup_get_all') 777 @mock.patch('cinder.context.get_admin_context') 778 def test_update_consisgroup_host(self, get_admin_context, 779 consisgroup_get_all, 780 consisgroup_update): 781 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 782 get_admin_context.return_value = ctxt 783 consisgroup = {'id': fake.CONSISTENCY_GROUP_ID, 784 'user_id': fake.USER_ID, 785 'project_id': fake.PROJECT_ID, 786 'host': 'fake-host', 787 'status': fields.ConsistencyGroupStatus.AVAILABLE 788 } 789 consisgroup_get_all.return_value = [consisgroup] 790 consisgrup_cmds = cinder_manage.ConsistencyGroupCommands() 791 consisgrup_cmds.update_cg_host('fake_host', 'fake_host2') 792 793 get_admin_context.assert_called_once_with() 794 consisgroup_get_all.assert_called_once_with( 795 ctxt, filters={'host': 'fake_host'}, limit=None, marker=None, 796 offset=None, sort_dirs=None, sort_keys=None) 797 consisgroup_update.assert_called_once_with( 798 ctxt, fake.CONSISTENCY_GROUP_ID, {'host': 'fake_host2'}) 799 800 @mock.patch('cinder.objects.service.Service.is_up', 801 new_callable=mock.PropertyMock) 802 @mock.patch('cinder.db.service_get_all') 803 @mock.patch('cinder.context.get_admin_context') 804 def _test_service_commands_list(self, service, get_admin_context, 805 service_get_all, service_is_up): 806 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 807 get_admin_context.return_value = ctxt 808 service_get_all.return_value = [service] 809 service_is_up.return_value = True 810 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 811 format = "%-16s %-36s %-16s %-10s %-5s %-20s %-12s %-15s %-36s" 812 print_format = format % ('Binary', 813 'Host', 814 'Zone', 815 'Status', 816 'State', 817 'Updated At', 818 'RPC Version', 819 'Object Version', 820 'Cluster') 821 rpc_version = service['rpc_current_version'] 822 object_version = service['object_current_version'] 823 cluster = service.get('cluster_name', '') 824 service_format = format % (service['binary'], 825 service['host'], 826 service['availability_zone'], 827 'enabled', 828 ':-)', 829 service['updated_at'], 830 rpc_version, 831 object_version, 832 cluster) 833 expected_out = print_format + '\n' + service_format + '\n' 834 835 service_cmds = cinder_manage.ServiceCommands() 836 service_cmds.list() 837 838 self.assertEqual(expected_out, fake_out.getvalue()) 839 get_admin_context.assert_called_with() 840 service_get_all.assert_called_with(ctxt) 841 842 def test_service_commands_list(self): 843 service = {'binary': 'cinder-binary', 844 'host': 'fake-host.fake-domain', 845 'availability_zone': 'fake-zone', 846 'updated_at': '2014-06-30 11:22:33', 847 'disabled': False, 848 'rpc_current_version': '1.1', 849 'object_current_version': '1.1', 850 'cluster_name': 'my_cluster', 851 'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'} 852 for binary in ('volume', 'scheduler', 'backup'): 853 service['binary'] = 'cinder-%s' % binary 854 self._test_service_commands_list(service) 855 856 def test_service_commands_list_no_updated_at_or_cluster(self): 857 service = {'binary': 'cinder-binary', 858 'host': 'fake-host.fake-domain', 859 'availability_zone': 'fake-zone', 860 'updated_at': None, 861 'disabled': False, 862 'rpc_current_version': '1.1', 863 'object_current_version': '1.1', 864 'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'} 865 for binary in ('volume', 'scheduler', 'backup'): 866 service['binary'] = 'cinder-%s' % binary 867 self._test_service_commands_list(service) 868 869 @ddt.data(('foobar', 'foobar'), ('-foo bar', 'foo bar'), 870 ('--foo bar', 'foo bar'), ('--foo-bar', 'foo_bar'), 871 ('---foo-bar', '_foo_bar')) 872 @ddt.unpack 873 def test_get_arg_string(self, arg, expected): 874 self.assertEqual(expected, cinder_manage.get_arg_string(arg)) 875 876 def test_fetch_func_args(self): 877 @cinder_manage.args('--full-rename') 878 @cinder_manage.args('--different-dest', dest='my_dest') 879 @cinder_manage.args('current') 880 def my_func(): 881 pass 882 883 expected = {'full_rename': mock.sentinel.full_rename, 884 'my_dest': mock.sentinel.my_dest, 885 'current': mock.sentinel.current} 886 887 with mock.patch.object(cinder_manage, 'CONF') as mock_conf: 888 mock_conf.category = mock.Mock(**expected) 889 self.assertDictEqual(expected, 890 cinder_manage.fetch_func_args(my_func)) 891 892 @mock.patch('cinder.context.get_admin_context') 893 @mock.patch('cinder.db.cluster_get_all') 894 def tests_cluster_commands_list(self, get_all_mock, get_admin_mock, 895 ): 896 now = timeutils.utcnow() 897 cluster = fake_cluster.fake_cluster_orm(num_hosts=4, num_down_hosts=2, 898 created_at=now, 899 last_heartbeat=now) 900 get_all_mock.return_value = [cluster] 901 902 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 903 get_admin_mock.return_value = ctxt 904 905 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 906 format_ = "%-36s %-16s %-10s %-5s %-20s %-7s %-12s %-20s" 907 print_format = format_ % ('Name', 908 'Binary', 909 'Status', 910 'State', 911 'Heartbeat', 912 'Hosts', 913 'Down Hosts', 914 'Updated At') 915 cluster_format = format_ % (cluster.name, cluster.binary, 916 'enabled', ':-)', 917 cluster.last_heartbeat, 918 cluster.num_hosts, 919 cluster.num_down_hosts, 920 None) 921 expected_out = print_format + '\n' + cluster_format + '\n' 922 923 cluster_cmds = cinder_manage.ClusterCommands() 924 cluster_cmds.list() 925 926 self.assertEqual(expected_out, fake_out.getvalue()) 927 get_admin_mock.assert_called_with() 928 get_all_mock.assert_called_with(ctxt, is_up=None, 929 get_services=False, 930 services_summary=True, 931 read_deleted='no') 932 933 @mock.patch('cinder.db.sqlalchemy.api.cluster_get', auto_specs=True) 934 @mock.patch('cinder.context.get_admin_context') 935 def test_cluster_commands_remove_not_found(self, admin_ctxt_mock, 936 cluster_get_mock): 937 cluster_get_mock.side_effect = exception.ClusterNotFound(id=1) 938 cluster_commands = cinder_manage.ClusterCommands() 939 exit = cluster_commands.remove(False, 'abinary', 'acluster') 940 self.assertEqual(2, exit) 941 cluster_get_mock.assert_called_once_with(admin_ctxt_mock.return_value, 942 None, name='acluster', 943 binary='abinary', 944 get_services=False) 945 946 @mock.patch('cinder.db.sqlalchemy.api.service_destroy', auto_specs=True) 947 @mock.patch('cinder.db.sqlalchemy.api.cluster_destroy', auto_specs=True) 948 @mock.patch('cinder.db.sqlalchemy.api.cluster_get', auto_specs=True) 949 @mock.patch('cinder.context.get_admin_context') 950 def test_cluster_commands_remove_fail_has_hosts(self, admin_ctxt_mock, 951 cluster_get_mock, 952 cluster_destroy_mock, 953 service_destroy_mock): 954 cluster = fake_cluster.fake_cluster_ovo(mock.Mock()) 955 cluster_get_mock.return_value = cluster 956 cluster_destroy_mock.side_effect = exception.ClusterHasHosts(id=1) 957 cluster_commands = cinder_manage.ClusterCommands() 958 exit = cluster_commands.remove(False, 'abinary', 'acluster') 959 self.assertEqual(2, exit) 960 cluster_get_mock.assert_called_once_with(admin_ctxt_mock.return_value, 961 None, name='acluster', 962 binary='abinary', 963 get_services=False) 964 cluster_destroy_mock.assert_called_once_with( 965 admin_ctxt_mock.return_value.elevated.return_value, cluster.id) 966 service_destroy_mock.assert_not_called() 967 968 @mock.patch('cinder.db.sqlalchemy.api.service_destroy', auto_specs=True) 969 @mock.patch('cinder.db.sqlalchemy.api.cluster_destroy', auto_specs=True) 970 @mock.patch('cinder.db.sqlalchemy.api.cluster_get', auto_specs=True) 971 @mock.patch('cinder.context.get_admin_context') 972 def test_cluster_commands_remove_success_no_hosts(self, admin_ctxt_mock, 973 cluster_get_mock, 974 cluster_destroy_mock, 975 service_destroy_mock): 976 cluster = fake_cluster.fake_cluster_orm() 977 cluster_get_mock.return_value = cluster 978 cluster_commands = cinder_manage.ClusterCommands() 979 exit = cluster_commands.remove(False, 'abinary', 'acluster') 980 self.assertIsNone(exit) 981 cluster_get_mock.assert_called_once_with(admin_ctxt_mock.return_value, 982 None, name='acluster', 983 binary='abinary', 984 get_services=False) 985 cluster_destroy_mock.assert_called_once_with( 986 admin_ctxt_mock.return_value.elevated.return_value, cluster.id) 987 service_destroy_mock.assert_not_called() 988 989 @mock.patch('cinder.db.sqlalchemy.api.service_destroy', auto_specs=True) 990 @mock.patch('cinder.db.sqlalchemy.api.cluster_destroy', auto_specs=True) 991 @mock.patch('cinder.db.sqlalchemy.api.cluster_get', auto_specs=True) 992 @mock.patch('cinder.context.get_admin_context') 993 def test_cluster_commands_remove_recursive(self, admin_ctxt_mock, 994 cluster_get_mock, 995 cluster_destroy_mock, 996 service_destroy_mock): 997 cluster = fake_cluster.fake_cluster_orm() 998 cluster.services = [fake_service.fake_service_orm()] 999 cluster_get_mock.return_value = cluster 1000 cluster_commands = cinder_manage.ClusterCommands() 1001 exit = cluster_commands.remove(True, 'abinary', 'acluster') 1002 self.assertIsNone(exit) 1003 cluster_get_mock.assert_called_once_with(admin_ctxt_mock.return_value, 1004 None, name='acluster', 1005 binary='abinary', 1006 get_services=True) 1007 cluster_destroy_mock.assert_called_once_with( 1008 admin_ctxt_mock.return_value.elevated.return_value, cluster.id) 1009 service_destroy_mock.assert_called_once_with( 1010 admin_ctxt_mock.return_value.elevated.return_value, 1011 cluster.services[0]['id']) 1012 1013 @mock.patch('cinder.db.sqlalchemy.api.volume_include_in_cluster', 1014 auto_specs=True, return_value=1) 1015 @mock.patch('cinder.db.sqlalchemy.api.consistencygroup_include_in_cluster', 1016 auto_specs=True, return_value=2) 1017 @mock.patch('cinder.context.get_admin_context') 1018 def test_cluster_commands_rename(self, admin_ctxt_mock, 1019 volume_include_mock, cg_include_mock): 1020 """Test that cluster rename changes volumes and cgs.""" 1021 current_cluster_name = mock.sentinel.old_cluster_name 1022 new_cluster_name = mock.sentinel.new_cluster_name 1023 partial = mock.sentinel.partial 1024 cluster_commands = cinder_manage.ClusterCommands() 1025 exit = cluster_commands.rename(partial, current_cluster_name, 1026 new_cluster_name) 1027 1028 self.assertIsNone(exit) 1029 volume_include_mock.assert_called_once_with( 1030 admin_ctxt_mock.return_value, new_cluster_name, partial, 1031 cluster_name=current_cluster_name) 1032 cg_include_mock.assert_called_once_with( 1033 admin_ctxt_mock.return_value, new_cluster_name, partial, 1034 cluster_name=current_cluster_name) 1035 1036 @mock.patch('cinder.db.sqlalchemy.api.volume_include_in_cluster', 1037 auto_specs=True, return_value=0) 1038 @mock.patch('cinder.db.sqlalchemy.api.consistencygroup_include_in_cluster', 1039 auto_specs=True, return_value=0) 1040 @mock.patch('cinder.context.get_admin_context') 1041 def test_cluster_commands_rename_no_changes(self, admin_ctxt_mock, 1042 volume_include_mock, 1043 cg_include_mock): 1044 """Test that we return an error when cluster rename has no effect.""" 1045 cluster_commands = cinder_manage.ClusterCommands() 1046 exit = cluster_commands.rename(False, 'cluster', 'new_cluster') 1047 self.assertEqual(2, exit) 1048 1049 @mock.patch('oslo_config.cfg.ConfigOpts.register_cli_opt') 1050 def test_main_argv_lt_2(self, register_cli_opt): 1051 script_name = 'cinder-manage' 1052 sys.argv = [script_name] 1053 CONF(sys.argv[1:], project='cinder', version=version.version_string()) 1054 1055 with mock.patch('sys.stdout', new=six.StringIO()): 1056 exit = self.assertRaises(SystemExit, cinder_manage.main) 1057 self.assertTrue(register_cli_opt.called) 1058 self.assertEqual(2, exit.code) 1059 1060 @mock.patch('oslo_config.cfg.ConfigOpts.__call__') 1061 @mock.patch('oslo_log.log.setup') 1062 @mock.patch('oslo_config.cfg.ConfigOpts.register_cli_opt') 1063 def test_main_sudo_failed(self, register_cli_opt, log_setup, 1064 config_opts_call): 1065 script_name = 'cinder-manage' 1066 sys.argv = [script_name, 'fake_category', 'fake_action'] 1067 config_opts_call.side_effect = cfg.ConfigFilesNotFoundError( 1068 mock.sentinel._namespace) 1069 1070 with mock.patch('sys.stdout', new=six.StringIO()): 1071 exit = self.assertRaises(SystemExit, cinder_manage.main) 1072 1073 self.assertTrue(register_cli_opt.called) 1074 config_opts_call.assert_called_once_with( 1075 sys.argv[1:], project='cinder', 1076 version=version.version_string()) 1077 self.assertFalse(log_setup.called) 1078 self.assertEqual(2, exit.code) 1079 1080 @mock.patch('oslo_config.cfg.ConfigOpts.__call__') 1081 @mock.patch('oslo_config.cfg.ConfigOpts.register_cli_opt') 1082 def test_main(self, register_cli_opt, config_opts_call): 1083 script_name = 'cinder-manage' 1084 sys.argv = [script_name, 'config', 'list'] 1085 action_fn = mock.MagicMock() 1086 CONF.category = mock.MagicMock(action_fn=action_fn) 1087 1088 cinder_manage.main() 1089 1090 self.assertTrue(register_cli_opt.called) 1091 config_opts_call.assert_called_once_with( 1092 sys.argv[1:], project='cinder', version=version.version_string()) 1093 self.assertTrue(action_fn.called) 1094 1095 @mock.patch('oslo_config.cfg.ConfigOpts.__call__') 1096 @mock.patch('oslo_log.log.setup') 1097 @mock.patch('oslo_config.cfg.ConfigOpts.register_cli_opt') 1098 def test_main_invalid_dir(self, register_cli_opt, log_setup, 1099 config_opts_call): 1100 script_name = 'cinder-manage' 1101 fake_dir = 'fake-dir' 1102 invalid_dir = 'Invalid directory:' 1103 sys.argv = [script_name, '--config-dir', fake_dir] 1104 config_opts_call.side_effect = cfg.ConfigDirNotFoundError(fake_dir) 1105 1106 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 1107 exit = self.assertRaises(SystemExit, cinder_manage.main) 1108 self.assertTrue(register_cli_opt.called) 1109 config_opts_call.assert_called_once_with( 1110 sys.argv[1:], project='cinder', 1111 version=version.version_string()) 1112 self.assertIn(invalid_dir, fake_out.getvalue()) 1113 self.assertIn(fake_dir, fake_out.getvalue()) 1114 self.assertFalse(log_setup.called) 1115 self.assertEqual(2, exit.code) 1116 1117 @mock.patch('cinder.db') 1118 def test_remove_service_failure(self, mock_db): 1119 mock_db.service_destroy.side_effect = SystemExit(1) 1120 service_commands = cinder_manage.ServiceCommands() 1121 exit = service_commands.remove('abinary', 'ahost') 1122 self.assertEqual(2, exit) 1123 1124 @mock.patch('cinder.db.service_destroy') 1125 @mock.patch( 1126 'cinder.db.service_get', 1127 return_value = {'id': '12', 1128 'uuid': 'a3a593da-7f8d-4bb7-8b4c-f2bc1e0b4824'}) 1129 def test_remove_service_success(self, mock_get_by_args, 1130 mock_service_destroy): 1131 service_commands = cinder_manage.ServiceCommands() 1132 self.assertIsNone(service_commands.remove('abinary', 'ahost')) 1133 1134 1135class TestCinderRtstoolCmd(test.TestCase): 1136 1137 def setUp(self): 1138 super(TestCinderRtstoolCmd, self).setUp() 1139 sys.argv = ['cinder-rtstool'] 1140 1141 self.INITIATOR_IQN = 'iqn.2015.12.com.example.openstack.i:UNIT1' 1142 self.TARGET_IQN = 'iqn.2015.12.com.example.openstack.i:TARGET1' 1143 1144 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1145 def test_create_rtslib_error(self, rtsroot): 1146 rtsroot.side_effect = rtslib_fb.utils.RTSLibError() 1147 1148 with mock.patch('sys.stdout', new=six.StringIO()): 1149 self.assertRaises(rtslib_fb.utils.RTSLibError, 1150 cinder_rtstool.create, 1151 mock.sentinel.backing_device, 1152 mock.sentinel.name, 1153 mock.sentinel.userid, 1154 mock.sentinel.password, 1155 mock.sentinel.iser_enabled) 1156 1157 def _test_create_rtslib_error_network_portal(self, ip): 1158 with mock.patch.object(rtslib_fb, 'NetworkPortal') as network_portal, \ 1159 mock.patch.object(rtslib_fb, 'LUN') as lun, \ 1160 mock.patch.object(rtslib_fb, 'TPG') as tpg, \ 1161 mock.patch.object(rtslib_fb, 'FabricModule') as fabric_module, \ 1162 mock.patch.object(rtslib_fb, 'Target') as target, \ 1163 mock.patch.object(rtslib_fb, 'BlockStorageObject') as \ 1164 block_storage_object, \ 1165 mock.patch.object(rtslib_fb.root, 'RTSRoot') as rts_root: 1166 root_new = mock.MagicMock(storage_objects=mock.MagicMock()) 1167 rts_root.return_value = root_new 1168 block_storage_object.return_value = mock.sentinel.so_new 1169 target.return_value = mock.sentinel.target_new 1170 fabric_module.return_value = mock.sentinel.fabric_new 1171 tpg_new = tpg.return_value 1172 lun.return_value = mock.sentinel.lun_new 1173 1174 if ip == '0.0.0.0': 1175 network_portal.side_effect = rtslib_fb.utils.RTSLibError() 1176 self.assertRaises(rtslib_fb.utils.RTSLibError, 1177 cinder_rtstool.create, 1178 mock.sentinel.backing_device, 1179 mock.sentinel.name, 1180 mock.sentinel.userid, 1181 mock.sentinel.password, 1182 mock.sentinel.iser_enabled) 1183 else: 1184 cinder_rtstool.create(mock.sentinel.backing_device, 1185 mock.sentinel.name, 1186 mock.sentinel.userid, 1187 mock.sentinel.password, 1188 mock.sentinel.iser_enabled) 1189 1190 rts_root.assert_called_once_with() 1191 block_storage_object.assert_called_once_with( 1192 name=mock.sentinel.name, dev=mock.sentinel.backing_device) 1193 target.assert_called_once_with(mock.sentinel.fabric_new, 1194 mock.sentinel.name, 'create') 1195 fabric_module.assert_called_once_with('iscsi') 1196 tpg.assert_called_once_with(mock.sentinel.target_new, 1197 mode='create') 1198 tpg_new.set_attribute.assert_called_once_with('authentication', 1199 '1') 1200 lun.assert_called_once_with(tpg_new, 1201 storage_object=mock.sentinel.so_new) 1202 self.assertEqual(1, tpg_new.enable) 1203 1204 if ip == '::0': 1205 ip = '[::0]' 1206 1207 network_portal.assert_any_call(tpg_new, ip, 3260, mode='any') 1208 1209 def test_create_rtslib_error_network_portal_ipv4(self): 1210 with mock.patch('sys.stdout', new=six.StringIO()): 1211 self._test_create_rtslib_error_network_portal('0.0.0.0') 1212 1213 def test_create_rtslib_error_network_portal_ipv6(self): 1214 with mock.patch('sys.stdout', new=six.StringIO()): 1215 self._test_create_rtslib_error_network_portal('::0') 1216 1217 def _test_create(self, ip): 1218 with mock.patch.object(rtslib_fb, 'NetworkPortal') as network_portal, \ 1219 mock.patch.object(rtslib_fb, 'LUN') as lun, \ 1220 mock.patch.object(rtslib_fb, 'TPG') as tpg, \ 1221 mock.patch.object(rtslib_fb, 'FabricModule') as fabric_module, \ 1222 mock.patch.object(rtslib_fb, 'Target') as target, \ 1223 mock.patch.object(rtslib_fb, 'BlockStorageObject') as \ 1224 block_storage_object, \ 1225 mock.patch.object(rtslib_fb.root, 'RTSRoot') as rts_root: 1226 root_new = mock.MagicMock(storage_objects=mock.MagicMock()) 1227 rts_root.return_value = root_new 1228 block_storage_object.return_value = mock.sentinel.so_new 1229 target.return_value = mock.sentinel.target_new 1230 fabric_module.return_value = mock.sentinel.fabric_new 1231 tpg_new = tpg.return_value 1232 lun.return_value = mock.sentinel.lun_new 1233 1234 cinder_rtstool.create(mock.sentinel.backing_device, 1235 mock.sentinel.name, 1236 mock.sentinel.userid, 1237 mock.sentinel.password, 1238 mock.sentinel.iser_enabled) 1239 1240 rts_root.assert_called_once_with() 1241 block_storage_object.assert_called_once_with( 1242 name=mock.sentinel.name, dev=mock.sentinel.backing_device) 1243 target.assert_called_once_with(mock.sentinel.fabric_new, 1244 mock.sentinel.name, 'create') 1245 fabric_module.assert_called_once_with('iscsi') 1246 tpg.assert_called_once_with(mock.sentinel.target_new, 1247 mode='create') 1248 tpg_new.set_attribute.assert_called_once_with('authentication', 1249 '1') 1250 lun.assert_called_once_with(tpg_new, 1251 storage_object=mock.sentinel.so_new) 1252 self.assertEqual(1, tpg_new.enable) 1253 1254 if ip == '::0': 1255 ip = '[::0]' 1256 1257 network_portal.assert_any_call(tpg_new, ip, 3260, mode='any') 1258 1259 def test_create_ipv4(self): 1260 self._test_create('0.0.0.0') 1261 1262 def test_create_ipv6(self): 1263 self._test_create('::0') 1264 1265 def _test_create_ips_and_port(self, mock_rtslib, port, ips, expected_ips): 1266 mock_rtslib.BlockStorageObject.return_value = mock.sentinel.bso 1267 mock_rtslib.Target.return_value = mock.sentinel.target_new 1268 mock_rtslib.FabricModule.return_value = mock.sentinel.iscsi_fabric 1269 tpg_new = mock_rtslib.TPG.return_value 1270 1271 cinder_rtstool.create(mock.sentinel.backing_device, 1272 mock.sentinel.name, 1273 mock.sentinel.userid, 1274 mock.sentinel.password, 1275 mock.sentinel.iser_enabled, 1276 portals_ips=ips, 1277 portals_port=port) 1278 1279 mock_rtslib.Target.assert_called_once_with(mock.sentinel.iscsi_fabric, 1280 mock.sentinel.name, 1281 'create') 1282 mock_rtslib.TPG.assert_called_once_with(mock.sentinel.target_new, 1283 mode='create') 1284 mock_rtslib.LUN.assert_called_once_with( 1285 tpg_new, 1286 storage_object=mock.sentinel.bso) 1287 1288 mock_rtslib.NetworkPortal.assert_has_calls( 1289 map(lambda ip: mock.call(tpg_new, ip, port, mode='any'), 1290 expected_ips), any_order=True 1291 ) 1292 1293 @mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True) 1294 def test_create_ips_and_port_ipv4(self, mock_rtslib): 1295 ips = ['10.0.0.2', '10.0.0.3', '10.0.0.4'] 1296 port = 3261 1297 self._test_create_ips_and_port(mock_rtslib, port, ips, ips) 1298 1299 @mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True) 1300 def test_create_ips_and_port_ipv6(self, mock_rtslib): 1301 ips = ['fe80::fc16:3eff:fecb:ad2f'] 1302 expected_ips = ['[fe80::fc16:3eff:fecb:ad2f]'] 1303 port = 3261 1304 self._test_create_ips_and_port(mock_rtslib, port, ips, 1305 expected_ips) 1306 1307 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1308 def test_add_initiator_rtslib_error(self, rtsroot): 1309 rtsroot.side_effect = rtslib_fb.utils.RTSLibError() 1310 1311 with mock.patch('sys.stdout', new=six.StringIO()): 1312 self.assertRaises(rtslib_fb.utils.RTSLibError, 1313 cinder_rtstool.add_initiator, 1314 mock.sentinel.target_iqn, 1315 self.INITIATOR_IQN, 1316 mock.sentinel.userid, 1317 mock.sentinel.password) 1318 1319 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1320 def test_add_initiator_rtstool_error(self, rtsroot): 1321 rtsroot.targets.return_value = {} 1322 1323 self.assertRaises(cinder_rtstool.RtstoolError, 1324 cinder_rtstool.add_initiator, 1325 mock.sentinel.target_iqn, 1326 self.INITIATOR_IQN, 1327 mock.sentinel.userid, 1328 mock.sentinel.password) 1329 1330 @mock.patch.object(rtslib_fb, 'MappedLUN') 1331 @mock.patch.object(rtslib_fb, 'NodeACL') 1332 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1333 def test_add_initiator_acl_exists(self, rtsroot, node_acl, mapped_lun): 1334 target_iqn = mock.MagicMock() 1335 target_iqn.tpgs.return_value = \ 1336 [{'node_acls': self.INITIATOR_IQN}] 1337 acl = mock.MagicMock(node_wwn=self.INITIATOR_IQN) 1338 tpg = mock.MagicMock(node_acls=[acl]) 1339 tpgs = iter([tpg]) 1340 target = mock.MagicMock(tpgs=tpgs, wwn=self.TARGET_IQN) 1341 rtsroot.return_value = mock.MagicMock(targets=[target]) 1342 1343 cinder_rtstool.add_initiator(self.TARGET_IQN, 1344 self.INITIATOR_IQN, 1345 mock.sentinel.userid, 1346 mock.sentinel.password) 1347 self.assertFalse(node_acl.called) 1348 self.assertFalse(mapped_lun.called) 1349 1350 @mock.patch.object(rtslib_fb, 'MappedLUN') 1351 @mock.patch.object(rtslib_fb, 'NodeACL') 1352 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1353 def test_add_initiator_acl_exists_case_1(self, 1354 rtsroot, 1355 node_acl, 1356 mapped_lun): 1357 """Ensure initiator iqns are handled in a case-insensitive manner.""" 1358 target_iqn = mock.MagicMock() 1359 target_iqn.tpgs.return_value = \ 1360 [{'node_acls': self.INITIATOR_IQN.lower()}] 1361 acl = mock.MagicMock(node_wwn=self.INITIATOR_IQN) 1362 tpg = mock.MagicMock(node_acls=[acl]) 1363 tpgs = iter([tpg]) 1364 target = mock.MagicMock(tpgs=tpgs, wwn=target_iqn) 1365 rtsroot.return_value = mock.MagicMock(targets=[target]) 1366 1367 cinder_rtstool.add_initiator(target_iqn, 1368 self.INITIATOR_IQN, 1369 mock.sentinel.userid, 1370 mock.sentinel.password) 1371 self.assertFalse(node_acl.called) 1372 self.assertFalse(mapped_lun.called) 1373 1374 @mock.patch.object(rtslib_fb, 'MappedLUN') 1375 @mock.patch.object(rtslib_fb, 'NodeACL') 1376 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1377 def test_add_initiator_acl_exists_case_2(self, 1378 rtsroot, 1379 node_acl, 1380 mapped_lun): 1381 """Ensure initiator iqns are handled in a case-insensitive manner.""" 1382 iqn_lower = self.INITIATOR_IQN.lower() 1383 target_iqn = mock.MagicMock() 1384 target_iqn.tpgs.return_value = \ 1385 [{'node_acls': self.INITIATOR_IQN}] 1386 acl = mock.MagicMock(node_wwn=iqn_lower) 1387 tpg = mock.MagicMock(node_acls=[acl]) 1388 tpgs = iter([tpg]) 1389 target = mock.MagicMock(tpgs=tpgs, wwn=target_iqn) 1390 rtsroot.return_value = mock.MagicMock(targets=[target]) 1391 1392 cinder_rtstool.add_initiator(target_iqn, 1393 self.INITIATOR_IQN, 1394 mock.sentinel.userid, 1395 mock.sentinel.password) 1396 self.assertFalse(node_acl.called) 1397 self.assertFalse(mapped_lun.called) 1398 1399 @mock.patch.object(rtslib_fb, 'MappedLUN') 1400 @mock.patch.object(rtslib_fb, 'NodeACL') 1401 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1402 def test_add_initiator(self, rtsroot, node_acl, mapped_lun): 1403 target_iqn = mock.MagicMock() 1404 target_iqn.tpgs.return_value = \ 1405 [{'node_acls': self.INITIATOR_IQN}] 1406 tpg = mock.MagicMock() 1407 tpgs = iter([tpg]) 1408 target = mock.MagicMock(tpgs=tpgs, wwn=target_iqn) 1409 rtsroot.return_value = mock.MagicMock(targets=[target]) 1410 1411 acl_new = mock.MagicMock(chap_userid=mock.sentinel.userid, 1412 chap_password=mock.sentinel.password) 1413 node_acl.return_value = acl_new 1414 1415 cinder_rtstool.add_initiator(target_iqn, 1416 self.INITIATOR_IQN, 1417 mock.sentinel.userid, 1418 mock.sentinel.password) 1419 node_acl.assert_called_once_with(tpg, 1420 self.INITIATOR_IQN, 1421 mode='create') 1422 mapped_lun.assert_called_once_with(acl_new, 0, tpg_lun=0) 1423 1424 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1425 def test_get_targets(self, rtsroot): 1426 target = mock.MagicMock() 1427 target.dump.return_value = {'wwn': 'fake-wwn'} 1428 rtsroot.return_value = mock.MagicMock(targets=[target]) 1429 1430 with mock.patch('sys.stdout', new=six.StringIO()) as fake_out: 1431 cinder_rtstool.get_targets() 1432 1433 self.assertEqual(str(target.wwn), fake_out.getvalue().strip()) 1434 1435 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1436 def test_delete(self, rtsroot): 1437 target = mock.MagicMock(wwn=mock.sentinel.iqn) 1438 storage_object = mock.MagicMock() 1439 name = mock.PropertyMock(return_value=mock.sentinel.iqn) 1440 type(storage_object).name = name 1441 rtsroot.return_value = mock.MagicMock( 1442 targets=[target], storage_objects=[storage_object]) 1443 1444 cinder_rtstool.delete(mock.sentinel.iqn) 1445 1446 target.delete.assert_called_once_with() 1447 storage_object.delete.assert_called_once_with() 1448 1449 @mock.patch.object(rtslib_fb, 'MappedLUN') 1450 @mock.patch.object(rtslib_fb, 'NodeACL') 1451 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1452 def test_delete_initiator(self, rtsroot, node_acl, mapped_lun): 1453 target_iqn = mock.MagicMock() 1454 target_iqn.tpgs.return_value = \ 1455 [{'node_acls': self.INITIATOR_IQN}] 1456 acl = mock.MagicMock(node_wwn=self.INITIATOR_IQN) 1457 tpg = mock.MagicMock(node_acls=[acl]) 1458 tpgs = iter([tpg]) 1459 target = mock.MagicMock(tpgs=tpgs, wwn=target_iqn) 1460 rtsroot.return_value = mock.MagicMock(targets=[target]) 1461 1462 cinder_rtstool.delete_initiator(target_iqn, 1463 self.INITIATOR_IQN) 1464 1465 @mock.patch.object(rtslib_fb, 'MappedLUN') 1466 @mock.patch.object(rtslib_fb, 'NodeACL') 1467 @mock.patch.object(rtslib_fb.root, 'RTSRoot') 1468 def test_delete_initiator_case(self, rtsroot, node_acl, mapped_lun): 1469 """Ensure iqns are handled in a case-insensitive manner.""" 1470 initiator_iqn_lower = self.INITIATOR_IQN.lower() 1471 target_iqn = mock.MagicMock() 1472 target_iqn.tpgs.return_value = \ 1473 [{'node_acls': initiator_iqn_lower}] 1474 acl = mock.MagicMock(node_wwn=self.INITIATOR_IQN) 1475 tpg = mock.MagicMock(node_acls=[acl]) 1476 tpgs = iter([tpg]) 1477 target = mock.MagicMock(tpgs=tpgs, wwn=target_iqn) 1478 rtsroot.return_value = mock.MagicMock(targets=[target]) 1479 1480 cinder_rtstool.delete_initiator(target_iqn, 1481 self.INITIATOR_IQN) 1482 1483 @mock.patch.object(cinder_rtstool, 'os', autospec=True) 1484 @mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True) 1485 def test_save_with_filename(self, mock_rtslib, mock_os): 1486 filename = mock.sentinel.filename 1487 cinder_rtstool.save_to_file(filename) 1488 rtsroot = mock_rtslib.root.RTSRoot 1489 rtsroot.assert_called_once_with() 1490 self.assertEqual(0, mock_os.path.dirname.call_count) 1491 self.assertEqual(0, mock_os.path.exists.call_count) 1492 self.assertEqual(0, mock_os.makedirs.call_count) 1493 rtsroot.return_value.save_to_file.assert_called_once_with(filename) 1494 1495 @mock.patch.object(cinder_rtstool, 'os', 1496 **{'path.exists.return_value': True, 1497 'path.dirname.return_value': mock.sentinel.dirname}) 1498 @mock.patch.object(cinder_rtstool, 'rtslib_fb', 1499 **{'root.default_save_file': mock.sentinel.filename}) 1500 def test_save(self, mock_rtslib, mock_os): 1501 """Test that we check path exists with default file.""" 1502 cinder_rtstool.save_to_file(None) 1503 rtsroot = mock_rtslib.root.RTSRoot 1504 rtsroot.assert_called_once_with() 1505 rtsroot.return_value.save_to_file.assert_called_once_with( 1506 mock.sentinel.filename) 1507 mock_os.path.dirname.assert_called_once_with(mock.sentinel.filename) 1508 mock_os.path.exists.assert_called_once_with(mock.sentinel.dirname) 1509 self.assertEqual(0, mock_os.makedirs.call_count) 1510 1511 @mock.patch.object(cinder_rtstool, 'os', 1512 **{'path.exists.return_value': False, 1513 'path.dirname.return_value': mock.sentinel.dirname}) 1514 @mock.patch.object(cinder_rtstool, 'rtslib_fb', 1515 **{'root.default_save_file': mock.sentinel.filename}) 1516 def test_save_no_targetcli(self, mock_rtslib, mock_os): 1517 """Test that we create path if it doesn't exist with default file.""" 1518 cinder_rtstool.save_to_file(None) 1519 rtsroot = mock_rtslib.root.RTSRoot 1520 rtsroot.assert_called_once_with() 1521 rtsroot.return_value.save_to_file.assert_called_once_with( 1522 mock.sentinel.filename) 1523 mock_os.path.dirname.assert_called_once_with(mock.sentinel.filename) 1524 mock_os.path.exists.assert_called_once_with(mock.sentinel.dirname) 1525 mock_os.makedirs.assert_called_once_with(mock.sentinel.dirname, 0o755) 1526 1527 @mock.patch.object(cinder_rtstool, 'os', autospec=True) 1528 @mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True) 1529 def test_save_error_creating_dir(self, mock_rtslib, mock_os): 1530 mock_os.path.dirname.return_value = 'dirname' 1531 mock_os.path.exists.return_value = False 1532 mock_os.makedirs.side_effect = OSError('error') 1533 1534 regexp = (r'targetcli not installed and could not create default ' 1535 r'directory \(dirname\): error$') 1536 self.assertRaisesRegexp(cinder_rtstool.RtstoolError, regexp, 1537 cinder_rtstool.save_to_file, None) 1538 1539 @mock.patch.object(cinder_rtstool, 'os', autospec=True) 1540 @mock.patch.object(cinder_rtstool, 'rtslib_fb', autospec=True) 1541 def test_save_error_saving(self, mock_rtslib, mock_os): 1542 save = mock_rtslib.root.RTSRoot.return_value.save_to_file 1543 save.side_effect = OSError('error') 1544 regexp = r'Could not save configuration to myfile: error' 1545 self.assertRaisesRegexp(cinder_rtstool.RtstoolError, regexp, 1546 cinder_rtstool.save_to_file, 'myfile') 1547 1548 @mock.patch.object(cinder_rtstool, 'rtslib_fb', 1549 **{'root.default_save_file': mock.sentinel.filename}) 1550 def test_restore(self, mock_rtslib): 1551 """Test that we restore target configuration with default file.""" 1552 cinder_rtstool.restore_from_file(None) 1553 rtsroot = mock_rtslib.root.RTSRoot 1554 rtsroot.assert_called_once_with() 1555 rtsroot.return_value.restore_from_file.assert_called_once_with( 1556 mock.sentinel.filename) 1557 1558 @mock.patch.object(cinder_rtstool, 'rtslib_fb') 1559 def test_restore_with_file(self, mock_rtslib): 1560 """Test that we restore target configuration with specified file.""" 1561 cinder_rtstool.restore_from_file('saved_file') 1562 rtsroot = mock_rtslib.root.RTSRoot 1563 rtsroot.return_value.restore_from_file.assert_called_once_with( 1564 'saved_file') 1565 1566 @mock.patch('cinder.cmd.rtstool.restore_from_file') 1567 def test_restore_error(self, restore_from_file): 1568 """Test that we fail to restore target configuration.""" 1569 restore_from_file.side_effect = OSError 1570 self.assertRaises(OSError, 1571 cinder_rtstool.restore_from_file, 1572 mock.sentinel.filename) 1573 1574 def test_usage(self): 1575 with mock.patch('sys.stdout', new=six.StringIO()): 1576 exit = self.assertRaises(SystemExit, cinder_rtstool.usage) 1577 self.assertEqual(1, exit.code) 1578 1579 @mock.patch('cinder.cmd.rtstool.usage') 1580 def test_main_argc_lt_2(self, usage): 1581 usage.side_effect = SystemExit(1) 1582 sys.argv = ['cinder-rtstool'] 1583 1584 exit = self.assertRaises(SystemExit, cinder_rtstool.usage) 1585 1586 self.assertTrue(usage.called) 1587 self.assertEqual(1, exit.code) 1588 1589 def test_main_create_argv_lt_6(self): 1590 sys.argv = ['cinder-rtstool', 'create'] 1591 self._test_main_check_argv() 1592 1593 def test_main_create_argv_gt_7(self): 1594 sys.argv = ['cinder-rtstool', 'create', 'fake-arg1', 'fake-arg2', 1595 'fake-arg3', 'fake-arg4', 'fake-arg5', 'fake-arg6'] 1596 self._test_main_check_argv() 1597 1598 def test_main_add_initiator_argv_lt_6(self): 1599 sys.argv = ['cinder-rtstool', 'add-initiator'] 1600 self._test_main_check_argv() 1601 1602 def test_main_delete_argv_lt_3(self): 1603 sys.argv = ['cinder-rtstool', 'delete'] 1604 self._test_main_check_argv() 1605 1606 def test_main_no_action(self): 1607 sys.argv = ['cinder-rtstool'] 1608 self._test_main_check_argv() 1609 1610 def _test_main_check_argv(self): 1611 with mock.patch('cinder.cmd.rtstool.usage') as usage: 1612 usage.side_effect = SystemExit(1) 1613 sys.argv = ['cinder-rtstool', 'create'] 1614 1615 exit = self.assertRaises(SystemExit, cinder_rtstool.main) 1616 1617 self.assertTrue(usage.called) 1618 self.assertEqual(1, exit.code) 1619 1620 @mock.patch('cinder.cmd.rtstool.save_to_file') 1621 def test_main_save(self, mock_save): 1622 sys.argv = ['cinder-rtstool', 1623 'save'] 1624 rc = cinder_rtstool.main() 1625 mock_save.assert_called_once_with(None) 1626 self.assertEqual(0, rc) 1627 1628 @mock.patch('cinder.cmd.rtstool.save_to_file') 1629 def test_main_save_with_file(self, mock_save): 1630 sys.argv = ['cinder-rtstool', 1631 'save', 1632 mock.sentinel.filename] 1633 rc = cinder_rtstool.main() 1634 mock_save.assert_called_once_with(mock.sentinel.filename) 1635 self.assertEqual(0, rc) 1636 1637 def test_main_create(self): 1638 with mock.patch('cinder.cmd.rtstool.create') as create: 1639 sys.argv = ['cinder-rtstool', 1640 'create', 1641 mock.sentinel.backing_device, 1642 mock.sentinel.name, 1643 mock.sentinel.userid, 1644 mock.sentinel.password, 1645 mock.sentinel.iser_enabled, 1646 str(mock.sentinel.initiator_iqns)] 1647 1648 rc = cinder_rtstool.main() 1649 1650 create.assert_called_once_with( 1651 mock.sentinel.backing_device, 1652 mock.sentinel.name, 1653 mock.sentinel.userid, 1654 mock.sentinel.password, 1655 mock.sentinel.iser_enabled, 1656 initiator_iqns=str(mock.sentinel.initiator_iqns)) 1657 self.assertEqual(0, rc) 1658 1659 @mock.patch('cinder.cmd.rtstool.create') 1660 def test_main_create_ips_and_port(self, mock_create): 1661 sys.argv = ['cinder-rtstool', 1662 'create', 1663 mock.sentinel.backing_device, 1664 mock.sentinel.name, 1665 mock.sentinel.userid, 1666 mock.sentinel.password, 1667 mock.sentinel.iser_enabled, 1668 str(mock.sentinel.initiator_iqns), 1669 '-p3261', 1670 '-aip1,ip2,ip3'] 1671 1672 rc = cinder_rtstool.main() 1673 1674 mock_create.assert_called_once_with( 1675 mock.sentinel.backing_device, 1676 mock.sentinel.name, 1677 mock.sentinel.userid, 1678 mock.sentinel.password, 1679 mock.sentinel.iser_enabled, 1680 initiator_iqns=str(mock.sentinel.initiator_iqns), 1681 portals_ips=['ip1', 'ip2', 'ip3'], 1682 portals_port=3261) 1683 self.assertEqual(0, rc) 1684 1685 def test_main_add_initiator(self): 1686 with mock.patch('cinder.cmd.rtstool.add_initiator') as add_initiator: 1687 sys.argv = ['cinder-rtstool', 1688 'add-initiator', 1689 mock.sentinel.target_iqn, 1690 mock.sentinel.userid, 1691 mock.sentinel.password, 1692 mock.sentinel.initiator_iqns] 1693 1694 rc = cinder_rtstool.main() 1695 1696 add_initiator.assert_called_once_with( 1697 mock.sentinel.target_iqn, mock.sentinel.initiator_iqns, 1698 mock.sentinel.userid, mock.sentinel.password) 1699 self.assertEqual(0, rc) 1700 1701 def test_main_get_targets(self): 1702 with mock.patch('cinder.cmd.rtstool.get_targets') as get_targets: 1703 sys.argv = ['cinder-rtstool', 'get-targets'] 1704 1705 rc = cinder_rtstool.main() 1706 1707 get_targets.assert_called_once_with() 1708 self.assertEqual(0, rc) 1709 1710 def test_main_delete(self): 1711 with mock.patch('cinder.cmd.rtstool.delete') as delete: 1712 sys.argv = ['cinder-rtstool', 'delete', mock.sentinel.iqn] 1713 1714 rc = cinder_rtstool.main() 1715 1716 delete.assert_called_once_with(mock.sentinel.iqn) 1717 self.assertEqual(0, rc) 1718 1719 @mock.patch.object(cinder_rtstool, 'verify_rtslib') 1720 def test_main_verify(self, mock_verify_rtslib): 1721 sys.argv = ['cinder-rtstool', 'verify'] 1722 1723 rc = cinder_rtstool.main() 1724 1725 mock_verify_rtslib.assert_called_once_with() 1726 self.assertEqual(0, rc) 1727 1728 1729class TestCinderVolumeUsageAuditCmd(test.TestCase): 1730 1731 def setUp(self): 1732 super(TestCinderVolumeUsageAuditCmd, self).setUp() 1733 sys.argv = ['cinder-volume-usage-audit'] 1734 1735 @mock.patch('cinder.utils.last_completed_audit_period') 1736 @mock.patch('cinder.rpc.init') 1737 @mock.patch('cinder.version.version_string') 1738 @mock.patch('oslo_log.log.getLogger') 1739 @mock.patch('oslo_log.log.setup') 1740 @mock.patch('cinder.context.get_admin_context') 1741 def test_main_time_error(self, get_admin_context, log_setup, get_logger, 1742 version_string, rpc_init, 1743 last_completed_audit_period): 1744 CONF.set_override('start_time', '2014-01-01 01:00:00') 1745 CONF.set_override('end_time', '2013-01-01 01:00:00') 1746 last_completed_audit_period.return_value = (mock.sentinel.begin, 1747 mock.sentinel.end) 1748 1749 exit = self.assertRaises(SystemExit, volume_usage_audit.main) 1750 1751 get_admin_context.assert_called_once_with() 1752 self.assertEqual('cinder', CONF.project) 1753 self.assertEqual(CONF.version, version.version_string()) 1754 log_setup.assert_called_once_with(CONF, "cinder") 1755 get_logger.assert_called_once_with('cinder') 1756 self.assertEqual(-1, exit.code) 1757 rpc_init.assert_called_once_with(CONF) 1758 last_completed_audit_period.assert_called_once_with() 1759 1760 @mock.patch('cinder.volume.utils.notify_about_volume_usage') 1761 @mock.patch('cinder.objects.volume.VolumeList.get_all_active_by_window') 1762 @mock.patch('cinder.utils.last_completed_audit_period') 1763 @mock.patch('cinder.rpc.init') 1764 @mock.patch('cinder.version.version_string') 1765 @mock.patch('oslo_log.log.getLogger') 1766 @mock.patch('oslo_log.log.setup') 1767 @mock.patch('cinder.context.get_admin_context') 1768 def test_main_send_create_volume_error(self, get_admin_context, log_setup, 1769 get_logger, version_string, 1770 rpc_init, 1771 last_completed_audit_period, 1772 volume_get_all_active_by_window, 1773 notify_about_volume_usage): 1774 CONF.set_override('send_actions', True) 1775 CONF.set_override('start_time', '2014-01-01 01:00:00') 1776 CONF.set_override('end_time', '2014-02-02 02:00:00') 1777 begin = datetime.datetime(2014, 1, 1, 1, 0, tzinfo=iso8601.UTC) 1778 end = datetime.datetime(2014, 2, 2, 2, 0, tzinfo=iso8601.UTC) 1779 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 1780 get_admin_context.return_value = ctxt 1781 last_completed_audit_period.return_value = (begin, end) 1782 volume1_created = datetime.datetime(2014, 1, 1, 2, 0, 1783 tzinfo=iso8601.UTC) 1784 volume1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 1785 tzinfo=iso8601.UTC) 1786 volume1 = mock.MagicMock(id=fake.VOLUME_ID, project_id=fake.PROJECT_ID, 1787 created_at=volume1_created, 1788 deleted_at=volume1_deleted) 1789 volume_get_all_active_by_window.return_value = [volume1] 1790 extra_info = { 1791 'audit_period_beginning': str(begin), 1792 'audit_period_ending': str(end), 1793 } 1794 local_extra_info = { 1795 'audit_period_beginning': str(volume1.created_at), 1796 'audit_period_ending': str(volume1.created_at), 1797 } 1798 1799 def _notify_about_volume_usage(*args, **kwargs): 1800 if 'create.end' in args: 1801 raise Exception() 1802 else: 1803 pass 1804 1805 notify_about_volume_usage.side_effect = _notify_about_volume_usage 1806 1807 volume_usage_audit.main() 1808 1809 get_admin_context.assert_called_once_with() 1810 self.assertEqual('cinder', CONF.project) 1811 self.assertEqual(CONF.version, version.version_string()) 1812 log_setup.assert_called_once_with(CONF, "cinder") 1813 get_logger.assert_called_once_with('cinder') 1814 rpc_init.assert_called_once_with(CONF) 1815 last_completed_audit_period.assert_called_once_with() 1816 volume_get_all_active_by_window.assert_called_once_with(ctxt, begin, 1817 end) 1818 notify_about_volume_usage.assert_has_calls([ 1819 mock.call(ctxt, volume1, 'exists', extra_usage_info=extra_info), 1820 mock.call(ctxt, volume1, 'create.start', 1821 extra_usage_info=local_extra_info), 1822 mock.call(ctxt, volume1, 'create.end', 1823 extra_usage_info=local_extra_info) 1824 ]) 1825 1826 @mock.patch('cinder.volume.utils.notify_about_volume_usage') 1827 @mock.patch('cinder.objects.volume.VolumeList.get_all_active_by_window') 1828 @mock.patch('cinder.utils.last_completed_audit_period') 1829 @mock.patch('cinder.rpc.init') 1830 @mock.patch('cinder.version.version_string') 1831 @mock.patch('oslo_log.log.getLogger') 1832 @mock.patch('oslo_log.log.setup') 1833 @mock.patch('cinder.context.get_admin_context') 1834 def test_main_send_delete_volume_error(self, get_admin_context, log_setup, 1835 get_logger, version_string, 1836 rpc_init, 1837 last_completed_audit_period, 1838 volume_get_all_active_by_window, 1839 notify_about_volume_usage): 1840 CONF.set_override('send_actions', True) 1841 CONF.set_override('start_time', '2014-01-01 01:00:00') 1842 CONF.set_override('end_time', '2014-02-02 02:00:00') 1843 begin = datetime.datetime(2014, 1, 1, 1, 0, tzinfo=iso8601.UTC) 1844 end = datetime.datetime(2014, 2, 2, 2, 0, tzinfo=iso8601.UTC) 1845 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 1846 get_admin_context.return_value = ctxt 1847 last_completed_audit_period.return_value = (begin, end) 1848 volume1_created = datetime.datetime(2014, 1, 1, 2, 0, 1849 tzinfo=iso8601.UTC) 1850 volume1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 1851 tzinfo=iso8601.UTC) 1852 volume1 = mock.MagicMock(id=fake.VOLUME_ID, project_id=fake.PROJECT_ID, 1853 created_at=volume1_created, 1854 deleted_at=volume1_deleted) 1855 volume_get_all_active_by_window.return_value = [volume1] 1856 extra_info = { 1857 'audit_period_beginning': str(begin), 1858 'audit_period_ending': str(end), 1859 } 1860 local_extra_info_create = { 1861 'audit_period_beginning': str(volume1.created_at), 1862 'audit_period_ending': str(volume1.created_at), 1863 } 1864 local_extra_info_delete = { 1865 'audit_period_beginning': str(volume1.deleted_at), 1866 'audit_period_ending': str(volume1.deleted_at), 1867 } 1868 1869 def _notify_about_volume_usage(*args, **kwargs): 1870 if 'delete.end' in args: 1871 raise Exception() 1872 else: 1873 pass 1874 1875 notify_about_volume_usage.side_effect = _notify_about_volume_usage 1876 1877 volume_usage_audit.main() 1878 1879 get_admin_context.assert_called_once_with() 1880 self.assertEqual('cinder', CONF.project) 1881 self.assertEqual(CONF.version, version.version_string()) 1882 log_setup.assert_called_once_with(CONF, "cinder") 1883 get_logger.assert_called_once_with('cinder') 1884 rpc_init.assert_called_once_with(CONF) 1885 last_completed_audit_period.assert_called_once_with() 1886 volume_get_all_active_by_window.assert_called_once_with(ctxt, begin, 1887 end) 1888 notify_about_volume_usage.assert_has_calls([ 1889 mock.call(ctxt, volume1, 'exists', extra_usage_info=extra_info), 1890 mock.call(ctxt, volume1, 'create.start', 1891 extra_usage_info=local_extra_info_create), 1892 mock.call(ctxt, volume1, 'create.end', 1893 extra_usage_info=local_extra_info_create), 1894 mock.call(ctxt, volume1, 'delete.start', 1895 extra_usage_info=local_extra_info_delete), 1896 mock.call(ctxt, volume1, 'delete.end', 1897 extra_usage_info=local_extra_info_delete) 1898 ]) 1899 1900 @mock.patch('cinder.volume.utils.notify_about_snapshot_usage') 1901 @mock.patch('cinder.objects.snapshot.SnapshotList.' 1902 'get_all_active_by_window') 1903 @mock.patch('cinder.volume.utils.notify_about_volume_usage') 1904 @mock.patch('cinder.objects.volume.VolumeList.get_all_active_by_window') 1905 @mock.patch('cinder.utils.last_completed_audit_period') 1906 @mock.patch('cinder.rpc.init') 1907 @mock.patch('cinder.version.version_string') 1908 @mock.patch('oslo_log.log.getLogger') 1909 @mock.patch('oslo_log.log.setup') 1910 @mock.patch('cinder.context.get_admin_context') 1911 def test_main_send_snapshot_error(self, get_admin_context, 1912 log_setup, get_logger, 1913 version_string, rpc_init, 1914 last_completed_audit_period, 1915 volume_get_all_active_by_window, 1916 notify_about_volume_usage, 1917 snapshot_get_all_active_by_window, 1918 notify_about_snapshot_usage): 1919 CONF.set_override('send_actions', True) 1920 CONF.set_override('start_time', '2014-01-01 01:00:00') 1921 CONF.set_override('end_time', '2014-02-02 02:00:00') 1922 begin = datetime.datetime(2014, 1, 1, 1, 0, tzinfo=iso8601.UTC) 1923 end = datetime.datetime(2014, 2, 2, 2, 0, tzinfo=iso8601.UTC) 1924 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 1925 get_admin_context.return_value = ctxt 1926 last_completed_audit_period.return_value = (begin, end) 1927 snapshot1_created = datetime.datetime(2014, 1, 1, 2, 0, 1928 tzinfo=iso8601.UTC) 1929 snapshot1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 1930 tzinfo=iso8601.UTC) 1931 snapshot1 = mock.MagicMock(id=fake.VOLUME_ID, 1932 project_id=fake.PROJECT_ID, 1933 created_at=snapshot1_created, 1934 deleted_at=snapshot1_deleted) 1935 volume_get_all_active_by_window.return_value = [] 1936 snapshot_get_all_active_by_window.return_value = [snapshot1] 1937 extra_info = { 1938 'audit_period_beginning': str(begin), 1939 'audit_period_ending': str(end), 1940 } 1941 local_extra_info_create = { 1942 'audit_period_beginning': str(snapshot1.created_at), 1943 'audit_period_ending': str(snapshot1.created_at), 1944 } 1945 local_extra_info_delete = { 1946 'audit_period_beginning': str(snapshot1.deleted_at), 1947 'audit_period_ending': str(snapshot1.deleted_at), 1948 } 1949 1950 def _notify_about_snapshot_usage(*args, **kwargs): 1951 # notify_about_snapshot_usage raises an exception, but does not 1952 # block 1953 raise Exception() 1954 1955 notify_about_snapshot_usage.side_effect = _notify_about_snapshot_usage 1956 1957 volume_usage_audit.main() 1958 1959 get_admin_context.assert_called_once_with() 1960 self.assertEqual('cinder', CONF.project) 1961 self.assertEqual(CONF.version, version.version_string()) 1962 log_setup.assert_called_once_with(CONF, "cinder") 1963 get_logger.assert_called_once_with('cinder') 1964 rpc_init.assert_called_once_with(CONF) 1965 last_completed_audit_period.assert_called_once_with() 1966 volume_get_all_active_by_window.assert_called_once_with(ctxt, begin, 1967 end) 1968 self.assertFalse(notify_about_volume_usage.called) 1969 notify_about_snapshot_usage.assert_has_calls([ 1970 mock.call(ctxt, snapshot1, 'exists', extra_info), 1971 mock.call(ctxt, snapshot1, 'create.start', 1972 extra_usage_info=local_extra_info_create), 1973 mock.call(ctxt, snapshot1, 'delete.start', 1974 extra_usage_info=local_extra_info_delete) 1975 ]) 1976 1977 @mock.patch('cinder.volume.utils.notify_about_backup_usage') 1978 @mock.patch('cinder.objects.backup.BackupList.get_all_active_by_window') 1979 @mock.patch('cinder.volume.utils.notify_about_volume_usage') 1980 @mock.patch('cinder.objects.volume.VolumeList.get_all_active_by_window') 1981 @mock.patch('cinder.utils.last_completed_audit_period') 1982 @mock.patch('cinder.rpc.init') 1983 @mock.patch('cinder.version.version_string') 1984 @mock.patch('cinder.context.get_admin_context') 1985 def test_main_send_backup_error(self, get_admin_context, 1986 version_string, rpc_init, 1987 last_completed_audit_period, 1988 volume_get_all_active_by_window, 1989 notify_about_volume_usage, 1990 backup_get_all_active_by_window, 1991 notify_about_backup_usage): 1992 CONF.set_override('send_actions', True) 1993 CONF.set_override('start_time', '2014-01-01 01:00:00') 1994 CONF.set_override('end_time', '2014-02-02 02:00:00') 1995 begin = datetime.datetime(2014, 1, 1, 1, 0, tzinfo=iso8601.UTC) 1996 end = datetime.datetime(2014, 2, 2, 2, 0, tzinfo=iso8601.UTC) 1997 ctxt = context.RequestContext('fake-user', 'fake-project') 1998 get_admin_context.return_value = ctxt 1999 last_completed_audit_period.return_value = (begin, end) 2000 backup1_created = datetime.datetime(2014, 1, 1, 2, 0, 2001 tzinfo=iso8601.UTC) 2002 backup1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 2003 tzinfo=iso8601.UTC) 2004 backup1 = mock.MagicMock(id=fake.BACKUP_ID, 2005 project_id=fake.PROJECT_ID, 2006 created_at=backup1_created, 2007 deleted_at=backup1_deleted) 2008 volume_get_all_active_by_window.return_value = [] 2009 backup_get_all_active_by_window.return_value = [backup1] 2010 extra_info = { 2011 'audit_period_beginning': str(begin), 2012 'audit_period_ending': str(end), 2013 } 2014 local_extra_info_create = { 2015 'audit_period_beginning': str(backup1.created_at), 2016 'audit_period_ending': str(backup1.created_at), 2017 } 2018 local_extra_info_delete = { 2019 'audit_period_beginning': str(backup1.deleted_at), 2020 'audit_period_ending': str(backup1.deleted_at), 2021 } 2022 2023 notify_about_backup_usage.side_effect = Exception() 2024 2025 volume_usage_audit.main() 2026 2027 get_admin_context.assert_called_once_with() 2028 self.assertEqual('cinder', CONF.project) 2029 self.assertEqual(CONF.version, version.version_string()) 2030 rpc_init.assert_called_once_with(CONF) 2031 last_completed_audit_period.assert_called_once_with() 2032 volume_get_all_active_by_window.assert_called_once_with(ctxt, 2033 begin, end) 2034 self.assertFalse(notify_about_volume_usage.called) 2035 notify_about_backup_usage.assert_any_call(ctxt, backup1, 'exists', 2036 extra_info) 2037 notify_about_backup_usage.assert_any_call( 2038 ctxt, backup1, 'create.start', 2039 extra_usage_info=local_extra_info_create) 2040 notify_about_backup_usage.assert_any_call( 2041 ctxt, backup1, 'delete.start', 2042 extra_usage_info=local_extra_info_delete) 2043 2044 @mock.patch('cinder.volume.utils.notify_about_backup_usage') 2045 @mock.patch('cinder.objects.backup.BackupList.get_all_active_by_window') 2046 @mock.patch('cinder.volume.utils.notify_about_snapshot_usage') 2047 @mock.patch('cinder.objects.snapshot.SnapshotList.' 2048 'get_all_active_by_window') 2049 @mock.patch('cinder.volume.utils.notify_about_volume_usage') 2050 @mock.patch('cinder.objects.volume.VolumeList.get_all_active_by_window') 2051 @mock.patch('cinder.utils.last_completed_audit_period') 2052 @mock.patch('cinder.rpc.init') 2053 @mock.patch('cinder.version.version_string') 2054 @mock.patch('oslo_log.log.getLogger') 2055 @mock.patch('oslo_log.log.setup') 2056 @mock.patch('cinder.context.get_admin_context') 2057 def test_main(self, get_admin_context, log_setup, get_logger, 2058 version_string, rpc_init, last_completed_audit_period, 2059 volume_get_all_active_by_window, notify_about_volume_usage, 2060 snapshot_get_all_active_by_window, 2061 notify_about_snapshot_usage, backup_get_all_active_by_window, 2062 notify_about_backup_usage): 2063 CONF.set_override('send_actions', True) 2064 CONF.set_override('start_time', '2014-01-01 01:00:00') 2065 CONF.set_override('end_time', '2014-02-02 02:00:00') 2066 begin = datetime.datetime(2014, 1, 1, 1, 0, tzinfo=iso8601.UTC) 2067 end = datetime.datetime(2014, 2, 2, 2, 0, tzinfo=iso8601.UTC) 2068 ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID) 2069 get_admin_context.return_value = ctxt 2070 last_completed_audit_period.return_value = (begin, end) 2071 2072 volume1_created = datetime.datetime(2014, 1, 1, 2, 0, 2073 tzinfo=iso8601.UTC) 2074 volume1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 2075 tzinfo=iso8601.UTC) 2076 volume1 = mock.MagicMock(id=fake.VOLUME_ID, project_id=fake.PROJECT_ID, 2077 created_at=volume1_created, 2078 deleted_at=volume1_deleted) 2079 volume_get_all_active_by_window.return_value = [volume1] 2080 extra_info = { 2081 'audit_period_beginning': str(begin), 2082 'audit_period_ending': str(end), 2083 } 2084 extra_info_volume_create = { 2085 'audit_period_beginning': str(volume1.created_at), 2086 'audit_period_ending': str(volume1.created_at), 2087 } 2088 extra_info_volume_delete = { 2089 'audit_period_beginning': str(volume1.deleted_at), 2090 'audit_period_ending': str(volume1.deleted_at), 2091 } 2092 2093 snapshot1_created = datetime.datetime(2014, 1, 1, 2, 0, 2094 tzinfo=iso8601.UTC) 2095 snapshot1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 2096 tzinfo=iso8601.UTC) 2097 snapshot1 = mock.MagicMock(id=fake.VOLUME_ID, 2098 project_id=fake.PROJECT_ID, 2099 created_at=snapshot1_created, 2100 deleted_at=snapshot1_deleted) 2101 snapshot_get_all_active_by_window.return_value = [snapshot1] 2102 extra_info_snapshot_create = { 2103 'audit_period_beginning': str(snapshot1.created_at), 2104 'audit_period_ending': str(snapshot1.created_at), 2105 } 2106 extra_info_snapshot_delete = { 2107 'audit_period_beginning': str(snapshot1.deleted_at), 2108 'audit_period_ending': str(snapshot1.deleted_at), 2109 } 2110 2111 backup1_created = datetime.datetime(2014, 1, 1, 2, 0, 2112 tzinfo=iso8601.UTC) 2113 backup1_deleted = datetime.datetime(2014, 1, 1, 3, 0, 2114 tzinfo=iso8601.UTC) 2115 backup1 = mock.MagicMock(id=fake.BACKUP_ID, 2116 project_id=fake.PROJECT_ID, 2117 created_at=backup1_created, 2118 deleted_at=backup1_deleted) 2119 backup_get_all_active_by_window.return_value = [backup1] 2120 extra_info_backup_create = { 2121 'audit_period_beginning': str(backup1.created_at), 2122 'audit_period_ending': str(backup1.created_at), 2123 } 2124 extra_info_backup_delete = { 2125 'audit_period_beginning': str(backup1.deleted_at), 2126 'audit_period_ending': str(backup1.deleted_at), 2127 } 2128 2129 volume_usage_audit.main() 2130 2131 get_admin_context.assert_called_once_with() 2132 self.assertEqual('cinder', CONF.project) 2133 self.assertEqual(CONF.version, version.version_string()) 2134 log_setup.assert_called_once_with(CONF, "cinder") 2135 get_logger.assert_called_once_with('cinder') 2136 rpc_init.assert_called_once_with(CONF) 2137 last_completed_audit_period.assert_called_once_with() 2138 volume_get_all_active_by_window.assert_called_once_with(ctxt, 2139 begin, end) 2140 notify_about_volume_usage.assert_has_calls([ 2141 mock.call(ctxt, volume1, 'exists', extra_usage_info=extra_info), 2142 mock.call(ctxt, volume1, 'create.start', 2143 extra_usage_info=extra_info_volume_create), 2144 mock.call(ctxt, volume1, 'create.end', 2145 extra_usage_info=extra_info_volume_create), 2146 mock.call(ctxt, volume1, 'delete.start', 2147 extra_usage_info=extra_info_volume_delete), 2148 mock.call(ctxt, volume1, 'delete.end', 2149 extra_usage_info=extra_info_volume_delete) 2150 ]) 2151 2152 notify_about_snapshot_usage.assert_has_calls([ 2153 mock.call(ctxt, snapshot1, 'exists', extra_info), 2154 mock.call(ctxt, snapshot1, 'create.start', 2155 extra_usage_info=extra_info_snapshot_create), 2156 mock.call(ctxt, snapshot1, 'create.end', 2157 extra_usage_info=extra_info_snapshot_create), 2158 mock.call(ctxt, snapshot1, 'delete.start', 2159 extra_usage_info=extra_info_snapshot_delete), 2160 mock.call(ctxt, snapshot1, 'delete.end', 2161 extra_usage_info=extra_info_snapshot_delete) 2162 ]) 2163 2164 notify_about_backup_usage.assert_has_calls([ 2165 mock.call(ctxt, backup1, 'exists', extra_info), 2166 mock.call(ctxt, backup1, 'create.start', 2167 extra_usage_info=extra_info_backup_create), 2168 mock.call(ctxt, backup1, 'create.end', 2169 extra_usage_info=extra_info_backup_create), 2170 mock.call(ctxt, backup1, 'delete.start', 2171 extra_usage_info=extra_info_backup_delete), 2172 mock.call(ctxt, backup1, 'delete.end', 2173 extra_usage_info=extra_info_backup_delete) 2174 ]) 2175 2176 2177class TestVolumeSharedTargetsOnlineMigration(test.TestCase): 2178 """Unit tests for cinder.db.api.service_*.""" 2179 2180 def setUp(self): 2181 super(TestVolumeSharedTargetsOnlineMigration, self).setUp() 2182 2183 def _get_minimum_rpc_version_mock(ctxt, binary): 2184 binary_map = { 2185 'cinder-volume': rpcapi.VolumeAPI, 2186 } 2187 return binary_map[binary].RPC_API_VERSION 2188 2189 self.patch('cinder.objects.Service.get_minimum_rpc_version', 2190 side_effect=_get_minimum_rpc_version_mock) 2191 2192 ctxt = context.get_admin_context() 2193 # default value in db for shared_targets on a volume 2194 # is True, so don't need to set it here explicitly 2195 for i in range(3): 2196 sqlalchemy_api.volume_create( 2197 ctxt, 2198 {'host': 'host1@lvm-driver1#lvm-driver1', 2199 'service_uuid': 'f080f895-cff2-4eb3-9c61-050c060b59ad'}) 2200 2201 values = { 2202 'host': 'host1@lvm-driver1', 2203 'binary': constants.VOLUME_BINARY, 2204 'topic': constants.VOLUME_TOPIC, 2205 'uuid': 'f080f895-cff2-4eb3-9c61-050c060b59ad'} 2206 utils.create_service(ctxt, values) 2207 self.ctxt = ctxt 2208 2209 @mock.patch('cinder.objects.Service.get_minimum_obj_version', 2210 return_value='1.8') 2211 def test_shared_targets_migrations(self, mock_version): 2212 """Ensure we can update the column.""" 2213 # Run the migration and verify that we updated 1 entry 2214 with mock.patch('cinder.volume.rpcapi.VolumeAPI.get_capabilities', 2215 return_value={'connection_protocol': 'iSCSI', 2216 'shared_targets': False}): 2217 total, updated = ( 2218 cinder_manage.shared_targets_online_data_migration( 2219 self.ctxt, 10)) 2220 self.assertEqual(3, total) 2221 self.assertEqual(3, updated) 2222 2223 @mock.patch('cinder.objects.Service.get_minimum_obj_version', 2224 return_value='1.8') 2225 def test_shared_targets_migrations_non_iscsi(self, mock_version): 2226 """Ensure we can update the column.""" 2227 # Run the migration and verify that we updated 1 entry 2228 with mock.patch('cinder.volume.rpcapi.VolumeAPI.get_capabilities', 2229 return_value={'connection_protocol': 'RBD'}): 2230 total, updated = ( 2231 cinder_manage.shared_targets_online_data_migration( 2232 self.ctxt, 10)) 2233 self.assertEqual(3, total) 2234 self.assertEqual(3, updated) 2235 2236 @mock.patch('cinder.objects.Service.get_minimum_obj_version', 2237 return_value='1.8') 2238 def test_shared_targets_migrations_with_limit(self, mock_version): 2239 """Ensure we update in batches.""" 2240 # Run the migration and verify that we updated 1 entry 2241 with mock.patch('cinder.volume.rpcapi.VolumeAPI.get_capabilities', 2242 return_value={'connection_protocol': 'iSCSI', 2243 'shared_targets': False}): 2244 total, updated = ( 2245 cinder_manage.shared_targets_online_data_migration( 2246 self.ctxt, 2)) 2247 self.assertEqual(3, total) 2248 self.assertEqual(2, updated) 2249 2250 total, updated = ( 2251 cinder_manage.shared_targets_online_data_migration( 2252 self.ctxt, 2)) 2253 self.assertEqual(1, total) 2254 self.assertEqual(1, updated) 2255