1# Copyright 2013 OpenStack Foundation 2# Copyright (C) 2013 Yahoo! Inc. 3# All Rights Reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may 6# not use this file except in compliance with the License. You may obtain 7# a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14# License for the specific language governing permissions and limitations 15# under the License. 16import argparse 17from copy import deepcopy 18import io 19import json 20import os 21from unittest import mock 22 23import sys 24import tempfile 25import testtools 26 27from glanceclient.common import utils 28from glanceclient import exc 29from glanceclient import shell 30 31# NOTE(geguileo): This is very nasty, but I can't find a better way to set 32# command line arguments in glanceclient.v2.shell.do_image_create that are 33# set by decorator utils.schema_args while preserving the spirits of the test 34 35# Backup original decorator 36original_schema_args = utils.schema_args 37 38 39# Set our own decorator that calls the original but with simulated schema 40def schema_args(schema_getter, omit=None): 41 global original_schema_args 42 # We only add the 2 arguments that are required by image-create 43 my_schema_getter = lambda: { 44 'properties': { 45 'container_format': { 46 'enum': [None, 'ami', 'ari', 'aki', 'bare', 'ovf', 'ova', 47 'docker'], 48 'type': 'string', 49 'description': 'Format of the container'}, 50 'disk_format': { 51 'enum': [None, 'ami', 'ari', 'aki', 'vhd', 'vhdx', 'vmdk', 52 'raw', 'qcow2', 'vdi', 'iso', 'ploop'], 53 'type': 'string', 54 'description': 'Format of the disk'}, 55 'location': {'type': 'string'}, 56 'locations': {'type': 'string'}, 57 'copy_from': {'type': 'string'}}} 58 return original_schema_args(my_schema_getter, omit) 59 60 61utils.schema_args = schema_args 62 63from glanceclient.v2 import shell as test_shell # noqa 64 65# Return original decorator. 66utils.schema_args = original_schema_args 67 68 69class ShellV2Test(testtools.TestCase): 70 def setUp(self): 71 super(ShellV2Test, self).setUp() 72 self._mock_utils() 73 self.gc = self._mock_glance_client() 74 self.shell = shell.OpenStackImagesShell() 75 os.environ = { 76 'OS_USERNAME': 'username', 77 'OS_PASSWORD': 'password', 78 'OS_TENANT_ID': 'tenant_id', 79 'OS_TOKEN_ID': 'test', 80 'OS_AUTH_URL': 'http://127.0.0.1:5000/v2.0/', 81 'OS_AUTH_TOKEN': 'pass', 82 'OS_IMAGE_API_VERSION': '1', 83 'OS_REGION_NAME': 'test', 84 'OS_IMAGE_URL': 'http://is.invalid'} 85 self.shell = shell.OpenStackImagesShell() 86 self.patched = mock.patch('glanceclient.common.utils.get_data_file', 87 autospec=True, return_value=None) 88 self.mock_get_data_file = self.patched.start() 89 90 def tearDown(self): 91 super(ShellV2Test, self).tearDown() 92 self.patched.stop() 93 94 def _make_args(self, args): 95 # NOTE(venkatesh): this conversion from a dict to an object 96 # is required because the test_shell.do_xxx(gc, args) methods 97 # expects the args to be attributes of an object. If passed as 98 # dict directly, it throws an AttributeError. 99 class Args(object): 100 def __init__(self, entries): 101 self.store = None 102 self.__dict__.update(entries) 103 104 return Args(args) 105 106 def _mock_glance_client(self): 107 my_mocked_gc = mock.Mock() 108 my_mocked_gc.schemas.return_value = 'test' 109 my_mocked_gc.get.return_value = {} 110 return my_mocked_gc 111 112 def _mock_utils(self): 113 utils.print_list = mock.Mock() 114 utils.print_dict = mock.Mock() 115 utils.save_image = mock.Mock() 116 utils.print_dict_list = mock.Mock() 117 118 def assert_exits_with_msg(self, func, func_args, err_msg=None): 119 with mock.patch.object(utils, 'exit') as mocked_utils_exit: 120 mocked_utils_exit.return_value = '%s' % err_msg 121 122 func(self.gc, func_args) 123 if err_msg: 124 mocked_utils_exit.assert_called_once_with(err_msg) 125 else: 126 mocked_utils_exit.assert_called_once_with() 127 128 def _run_command(self, cmd): 129 self.shell.main(cmd.split()) 130 131 stores_info_response = { 132 "stores": [ 133 { 134 "default": "true", 135 "id": "ceph1", 136 "description": "RBD backend for glance." 137 }, 138 { 139 "id": "file2", 140 "description": "Filesystem backend for glance." 141 }, 142 { 143 "id": "file1", 144 "description": "Filesystem backend for gkance." 145 }, 146 { 147 "id": "ceph2", 148 "description": "RBD backend for glance." 149 } 150 ] 151 } 152 153 def test_do_stores_info(self): 154 args = [] 155 with mock.patch.object(self.gc.images, 156 'get_stores_info') as mocked_list: 157 mocked_list.return_value = self.stores_info_response 158 159 test_shell.do_stores_info(self.gc, args) 160 161 mocked_list.assert_called_once_with() 162 utils.print_dict.assert_called_once_with(self.stores_info_response) 163 164 @mock.patch('glanceclient.common.utils.exit') 165 @mock.patch('sys.stdin', autospec=True) 166 def test_neg_stores_info( 167 self, mock_stdin, mock_utils_exit): 168 expected_msg = ('Multi Backend support is not enabled') 169 args = [] 170 mock_utils_exit.side_effect = self._mock_utils_exit 171 with mock.patch.object(self.gc.images, 172 'get_stores_info') as mocked_info: 173 mocked_info.side_effect = exc.HTTPNotFound 174 try: 175 test_shell.do_stores_info(self.gc, args) 176 self.fail("utils.exit should have been called") 177 except SystemExit: 178 pass 179 mock_utils_exit.assert_called_once_with(expected_msg) 180 181 @mock.patch('sys.stderr') 182 def test_image_create_missing_disk_format(self, __): 183 e = self.assertRaises(exc.CommandError, self._run_command, 184 '--os-image-api-version 2 image-create ' + 185 '--file fake_src --container-format bare') 186 self.assertEqual('error: Must provide --disk-format when using ' 187 '--file.', e.message) 188 189 @mock.patch('sys.stderr') 190 def test_image_create_missing_container_format(self, __): 191 e = self.assertRaises(exc.CommandError, self._run_command, 192 '--os-image-api-version 2 image-create ' + 193 '--file fake_src --disk-format qcow2') 194 self.assertEqual('error: Must provide --container-format when ' 195 'using --file.', e.message) 196 197 @mock.patch('sys.stderr') 198 def test_image_create_missing_container_format_stdin_data(self, __): 199 # Fake that get_data_file method returns data 200 self.mock_get_data_file.return_value = io.StringIO() 201 e = self.assertRaises(exc.CommandError, self._run_command, 202 '--os-image-api-version 2 image-create' 203 ' --disk-format qcow2') 204 self.assertEqual('error: Must provide --container-format when ' 205 'using stdin.', e.message) 206 207 @mock.patch('sys.stderr') 208 def test_image_create_missing_disk_format_stdin_data(self, __): 209 # Fake that get_data_file method returns data 210 self.mock_get_data_file.return_value = io.StringIO() 211 e = self.assertRaises(exc.CommandError, self._run_command, 212 '--os-image-api-version 2 image-create' 213 ' --container-format bare') 214 self.assertEqual('error: Must provide --disk-format when using stdin.', 215 e.message) 216 217 @mock.patch('sys.stderr') 218 def test_create_via_import_glance_direct_missing_disk_format(self, __): 219 e = self.assertRaises(exc.CommandError, self._run_command, 220 '--os-image-api-version 2 ' 221 'image-create-via-import ' 222 '--file fake_src --container-format bare') 223 self.assertEqual('error: Must provide --disk-format when using ' 224 '--file.', e.message) 225 226 @mock.patch('sys.stderr') 227 def test_create_via_import_glance_direct_missing_container_format( 228 self, __): 229 e = self.assertRaises(exc.CommandError, self._run_command, 230 '--os-image-api-version 2 ' 231 'image-create-via-import ' 232 '--file fake_src --disk-format qcow2') 233 self.assertEqual('error: Must provide --container-format when ' 234 'using --file.', e.message) 235 236 @mock.patch('sys.stderr') 237 def test_create_via_import_web_download_missing_disk_format(self, __): 238 e = self.assertRaises(exc.CommandError, self._run_command, 239 '--os-image-api-version 2 ' 240 'image-create-via-import ' + 241 '--import-method web-download ' + 242 '--uri fake_uri --container-format bare') 243 self.assertEqual('error: Must provide --disk-format when using ' 244 '--uri.', e.message) 245 246 @mock.patch('sys.stderr') 247 def test_create_via_import_web_download_missing_container_format( 248 self, __): 249 e = self.assertRaises(exc.CommandError, self._run_command, 250 '--os-image-api-version 2 ' 251 'image-create-via-import ' 252 '--import-method web-download ' 253 '--uri fake_uri --disk-format qcow2') 254 self.assertEqual('error: Must provide --container-format when ' 255 'using --uri.', e.message) 256 257 def test_do_image_list(self): 258 input = { 259 'limit': None, 260 'page_size': 18, 261 'visibility': True, 262 'member_status': 'Fake', 263 'owner': 'test', 264 'checksum': 'fake_checksum', 265 'tag': 'fake tag', 266 'properties': [], 267 'sort_key': ['name', 'id'], 268 'sort_dir': ['desc', 'asc'], 269 'sort': None, 270 'verbose': False, 271 'include_stores': False, 272 'os_hash_value': None, 273 'os_hidden': False 274 } 275 args = self._make_args(input) 276 with mock.patch.object(self.gc.images, 'list') as mocked_list: 277 mocked_list.return_value = {} 278 279 test_shell.do_image_list(self.gc, args) 280 281 exp_img_filters = { 282 'owner': 'test', 283 'member_status': 'Fake', 284 'visibility': True, 285 'checksum': 'fake_checksum', 286 'tag': 'fake tag', 287 'os_hidden': False 288 } 289 mocked_list.assert_called_once_with(page_size=18, 290 sort_key=['name', 'id'], 291 sort_dir=['desc', 'asc'], 292 filters=exp_img_filters) 293 utils.print_list.assert_called_once_with({}, ['ID', 'Name']) 294 295 def test_do_image_list_verbose(self): 296 input = { 297 'limit': None, 298 'page_size': 18, 299 'visibility': True, 300 'member_status': 'Fake', 301 'owner': 'test', 302 'checksum': 'fake_checksum', 303 'tag': 'fake tag', 304 'properties': [], 305 'sort_key': ['name', 'id'], 306 'sort_dir': ['desc', 'asc'], 307 'sort': None, 308 'verbose': True, 309 'include_stores': False, 310 'os_hash_value': None, 311 'os_hidden': False 312 } 313 args = self._make_args(input) 314 with mock.patch.object(self.gc.images, 'list') as mocked_list: 315 mocked_list.return_value = {} 316 317 test_shell.do_image_list(self.gc, args) 318 utils.print_list.assert_called_once_with( 319 {}, ['ID', 'Name', 'Disk_format', 'Container_format', 320 'Size', 'Status', 'Owner']) 321 322 def test_do_image_list_with_include_stores_true(self): 323 input = { 324 'limit': None, 325 'page_size': 18, 326 'visibility': True, 327 'member_status': 'Fake', 328 'owner': 'test', 329 'checksum': 'fake_checksum', 330 'tag': 'fake tag', 331 'properties': [], 332 'sort_key': ['name', 'id'], 333 'sort_dir': ['desc', 'asc'], 334 'sort': None, 335 'verbose': False, 336 'include_stores': True, 337 'os_hash_value': None, 338 'os_hidden': False 339 } 340 args = self._make_args(input) 341 with mock.patch.object(self.gc.images, 'list') as mocked_list: 342 mocked_list.return_value = {} 343 344 test_shell.do_image_list(self.gc, args) 345 utils.print_list.assert_called_once_with( 346 {}, ['ID', 'Name', 'Stores']) 347 348 def test_do_image_list_verbose_with_include_stores_true(self): 349 input = { 350 'limit': None, 351 'page_size': 18, 352 'visibility': True, 353 'member_status': 'Fake', 354 'owner': 'test', 355 'checksum': 'fake_checksum', 356 'tag': 'fake tag', 357 'properties': [], 358 'sort_key': ['name', 'id'], 359 'sort_dir': ['desc', 'asc'], 360 'sort': None, 361 'verbose': True, 362 'include_stores': True, 363 'os_hash_value': None, 364 'os_hidden': False 365 } 366 args = self._make_args(input) 367 with mock.patch.object(self.gc.images, 'list') as mocked_list: 368 mocked_list.return_value = {} 369 370 test_shell.do_image_list(self.gc, args) 371 utils.print_list.assert_called_once_with( 372 {}, ['ID', 'Name', 'Disk_format', 'Container_format', 373 'Size', 'Status', 'Owner', 'Stores']) 374 375 def test_do_image_list_with_hidden_true(self): 376 input = { 377 'limit': None, 378 'page_size': 18, 379 'visibility': True, 380 'member_status': 'Fake', 381 'owner': 'test', 382 'checksum': 'fake_checksum', 383 'tag': 'fake tag', 384 'properties': [], 385 'sort_key': ['name', 'id'], 386 'sort_dir': ['desc', 'asc'], 387 'sort': None, 388 'verbose': False, 389 'include_stores': False, 390 'os_hash_value': None, 391 'os_hidden': True 392 } 393 args = self._make_args(input) 394 with mock.patch.object(self.gc.images, 'list') as mocked_list: 395 mocked_list.return_value = {} 396 397 test_shell.do_image_list(self.gc, args) 398 399 exp_img_filters = { 400 'owner': 'test', 401 'member_status': 'Fake', 402 'visibility': True, 403 'checksum': 'fake_checksum', 404 'tag': 'fake tag', 405 'os_hidden': True 406 } 407 mocked_list.assert_called_once_with(page_size=18, 408 sort_key=['name', 'id'], 409 sort_dir=['desc', 'asc'], 410 filters=exp_img_filters) 411 utils.print_list.assert_called_once_with({}, ['ID', 'Name']) 412 413 def test_do_image_list_with_single_sort_key(self): 414 input = { 415 'limit': None, 416 'page_size': 18, 417 'visibility': True, 418 'member_status': 'Fake', 419 'owner': 'test', 420 'checksum': 'fake_checksum', 421 'tag': 'fake tag', 422 'properties': [], 423 'sort_key': ['name'], 424 'sort_dir': ['desc'], 425 'sort': None, 426 'verbose': False, 427 'include_stores': False, 428 'os_hash_value': None, 429 'os_hidden': False 430 } 431 args = self._make_args(input) 432 with mock.patch.object(self.gc.images, 'list') as mocked_list: 433 mocked_list.return_value = {} 434 435 test_shell.do_image_list(self.gc, args) 436 437 exp_img_filters = { 438 'owner': 'test', 439 'member_status': 'Fake', 440 'visibility': True, 441 'checksum': 'fake_checksum', 442 'tag': 'fake tag', 443 'os_hidden': False 444 } 445 mocked_list.assert_called_once_with(page_size=18, 446 sort_key=['name'], 447 sort_dir=['desc'], 448 filters=exp_img_filters) 449 utils.print_list.assert_called_once_with({}, ['ID', 'Name']) 450 451 def test_do_image_list_new_sorting_syntax(self): 452 input = { 453 'limit': None, 454 'page_size': 18, 455 'visibility': True, 456 'member_status': 'Fake', 457 'owner': 'test', 458 'checksum': 'fake_checksum', 459 'tag': 'fake tag', 460 'properties': [], 461 'sort': 'name:desc,size:asc', 462 'sort_key': [], 463 'sort_dir': [], 464 'verbose': False, 465 'include_stores': False, 466 'os_hash_value': None, 467 'os_hidden': False 468 } 469 args = self._make_args(input) 470 with mock.patch.object(self.gc.images, 'list') as mocked_list: 471 mocked_list.return_value = {} 472 473 test_shell.do_image_list(self.gc, args) 474 475 exp_img_filters = { 476 'owner': 'test', 477 'member_status': 'Fake', 478 'visibility': True, 479 'checksum': 'fake_checksum', 480 'tag': 'fake tag', 481 'os_hidden': False 482 } 483 mocked_list.assert_called_once_with( 484 page_size=18, 485 sort='name:desc,size:asc', 486 filters=exp_img_filters) 487 utils.print_list.assert_called_once_with({}, ['ID', 'Name']) 488 489 def test_do_image_list_with_property_filter(self): 490 input = { 491 'limit': None, 492 'page_size': 1, 493 'visibility': True, 494 'member_status': 'Fake', 495 'owner': 'test', 496 'checksum': 'fake_checksum', 497 'tag': 'fake tag', 498 'properties': ['os_distro=NixOS', 'architecture=x86_64'], 499 'sort_key': ['name'], 500 'sort_dir': ['desc'], 501 'sort': None, 502 'verbose': False, 503 'include_stores': False, 504 'os_hash_value': None, 505 'os_hidden': False 506 } 507 args = self._make_args(input) 508 with mock.patch.object(self.gc.images, 'list') as mocked_list: 509 mocked_list.return_value = {} 510 511 test_shell.do_image_list(self.gc, args) 512 513 exp_img_filters = { 514 'owner': 'test', 515 'member_status': 'Fake', 516 'visibility': True, 517 'checksum': 'fake_checksum', 518 'tag': 'fake tag', 519 'os_distro': 'NixOS', 520 'architecture': 'x86_64', 521 'os_hidden': False 522 } 523 524 mocked_list.assert_called_once_with(page_size=1, 525 sort_key=['name'], 526 sort_dir=['desc'], 527 filters=exp_img_filters) 528 utils.print_list.assert_called_once_with({}, ['ID', 'Name']) 529 530 def test_do_image_show_human_readable(self): 531 args = self._make_args({'id': 'pass', 'page_size': 18, 532 'human_readable': True, 533 'max_column_width': 120}) 534 with mock.patch.object(self.gc.images, 'get') as mocked_list: 535 ignore_fields = ['self', 'access', 'file', 'schema'] 536 expect_image = dict([(field, field) for field in ignore_fields]) 537 expect_image['id'] = 'pass' 538 expect_image['size'] = 1024 539 mocked_list.return_value = expect_image 540 541 test_shell.do_image_show(self.gc, args) 542 543 mocked_list.assert_called_once_with('pass') 544 utils.print_dict.assert_called_once_with({'id': 'pass', 545 'size': '1kB'}, 546 max_column_width=120) 547 548 def test_do_image_show(self): 549 args = self._make_args({'id': 'pass', 'page_size': 18, 550 'human_readable': False, 551 'max_column_width': 120}) 552 with mock.patch.object(self.gc.images, 'get') as mocked_list: 553 ignore_fields = ['self', 'access', 'file', 'schema'] 554 expect_image = dict([(field, field) for field in ignore_fields]) 555 expect_image['id'] = 'pass' 556 expect_image['size'] = 1024 557 mocked_list.return_value = expect_image 558 559 test_shell.do_image_show(self.gc, args) 560 561 mocked_list.assert_called_once_with('pass') 562 utils.print_dict.assert_called_once_with({'id': 'pass', 563 'size': 1024}, 564 max_column_width=120) 565 566 def _test_do_image_tasks(self, verbose=False, supported=True): 567 args = self._make_args({'id': 'pass', 'verbose': verbose}) 568 expected_columns = ["Message", "Status", "Updated at"] 569 expected_output = { 570 "tasks": [ 571 { 572 "image_id": "pass", 573 "id": "task_1", 574 "user_id": "user_1", 575 "request_id": "request_id_1", 576 "message": "fake_message", 577 "status": "status", 578 } 579 ] 580 } 581 582 if verbose: 583 columns_to_prepend = ['Image Id', 'Task Id'] 584 columns_to_extend = ['User Id', 'Request Id', 585 'Result', 'Owner', 'Input', 'Expires at'] 586 expected_columns = (columns_to_prepend + expected_columns + 587 columns_to_extend) 588 expected_output["tasks"][0]["Result"] = "Fake Result" 589 expected_output["tasks"][0]["Owner"] = "Fake Owner" 590 expected_output["tasks"][0]["Input"] = "Fake Input" 591 expected_output["tasks"][0]["Expires at"] = "Fake Expiry" 592 593 with mock.patch.object(self.gc.images, 594 'get_associated_image_tasks') as mocked_tasks: 595 if supported: 596 mocked_tasks.return_value = expected_output 597 else: 598 mocked_tasks.side_effect = exc.HTTPNotImplemented 599 test_shell.do_image_tasks(self.gc, args) 600 mocked_tasks.assert_called_once_with('pass') 601 if supported: 602 utils.print_dict_list.assert_called_once_with( 603 expected_output['tasks'], expected_columns) 604 605 def test_do_image_tasks_without_verbose(self): 606 self._test_do_image_tasks() 607 608 def test_do_image_tasks_with_verbose(self): 609 self._test_do_image_tasks(verbose=True) 610 611 def test_do_image_tasks_unsupported(self): 612 with mock.patch('glanceclient.common.utils.exit') as mock_exit: 613 self._test_do_image_tasks(supported=False) 614 mock_exit.assert_called_once_with( 615 'Server does not support image tasks API (v2.12)') 616 617 @mock.patch('sys.stdin', autospec=True) 618 def test_do_image_create_no_user_props(self, mock_stdin): 619 args = self._make_args({'name': 'IMG-01', 'disk_format': 'vhd', 620 'container_format': 'bare', 621 'file': None}) 622 with mock.patch.object(self.gc.images, 'create') as mocked_create: 623 ignore_fields = ['self', 'access', 'file', 'schema'] 624 expect_image = dict([(field, field) for field in ignore_fields]) 625 expect_image['id'] = 'pass' 626 expect_image['name'] = 'IMG-01' 627 expect_image['disk_format'] = 'vhd' 628 expect_image['container_format'] = 'bare' 629 mocked_create.return_value = expect_image 630 631 # Ensure that the test stdin is not considered 632 # to be supplying image data 633 mock_stdin.isatty = lambda: True 634 test_shell.do_image_create(self.gc, args) 635 636 mocked_create.assert_called_once_with(name='IMG-01', 637 disk_format='vhd', 638 container_format='bare') 639 utils.print_dict.assert_called_once_with({ 640 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 641 'container_format': 'bare'}) 642 643 @mock.patch('sys.stdin', autospec=True) 644 def test_do_image_create_for_none_multi_hash(self, mock_stdin): 645 args = self._make_args({'name': 'IMG-01', 'disk_format': 'vhd', 646 'container_format': 'bare', 647 'file': None}) 648 with mock.patch.object(self.gc.images, 'create') as mocked_create: 649 ignore_fields = ['self', 'access', 'file', 'schema'] 650 expect_image = dict([(field, field) for field in ignore_fields]) 651 expect_image['id'] = 'pass' 652 expect_image['name'] = 'IMG-01' 653 expect_image['disk_format'] = 'vhd' 654 expect_image['container_format'] = 'bare' 655 expect_image['os_hash_algo'] = None 656 expect_image['os_hash_value'] = None 657 mocked_create.return_value = expect_image 658 659 # Ensure that the test stdin is not considered 660 # to be supplying image data 661 mock_stdin.isatty = lambda: True 662 test_shell.do_image_create(self.gc, args) 663 664 mocked_create.assert_called_once_with(name='IMG-01', 665 disk_format='vhd', 666 container_format='bare') 667 utils.print_dict.assert_called_once_with({ 668 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 669 'container_format': 'bare', 'os_hash_algo': None, 670 'os_hash_value': None}) 671 672 def test_do_image_create_with_multihash(self): 673 self.mock_get_data_file.return_value = io.StringIO() 674 try: 675 with open(tempfile.mktemp(), 'w+') as f: 676 f.write('Some data here') 677 f.flush() 678 f.seek(0) 679 file_name = f.name 680 temp_args = {'name': 'IMG-01', 681 'disk_format': 'vhd', 682 'container_format': 'bare', 683 'file': file_name, 684 'progress': False} 685 args = self._make_args(temp_args) 686 with mock.patch.object(self.gc.images, 'create') as mocked_create: 687 with mock.patch.object(self.gc.images, 'get') as mocked_get: 688 689 ignore_fields = ['self', 'access', 'schema'] 690 expect_image = dict([(field, field) for field in 691 ignore_fields]) 692 expect_image['id'] = 'pass' 693 expect_image['name'] = 'IMG-01' 694 expect_image['disk_format'] = 'vhd' 695 expect_image['container_format'] = 'bare' 696 expect_image['checksum'] = 'fake-checksum' 697 expect_image['os_hash_algo'] = 'fake-hash_algo' 698 expect_image['os_hash_value'] = 'fake-hash_value' 699 mocked_create.return_value = expect_image 700 mocked_get.return_value = expect_image 701 702 test_shell.do_image_create(self.gc, args) 703 704 temp_args.pop('file', None) 705 mocked_create.assert_called_once_with(**temp_args) 706 mocked_get.assert_called_once_with('pass') 707 utils.print_dict.assert_called_once_with({ 708 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 709 'container_format': 'bare', 710 'checksum': 'fake-checksum', 711 'os_hash_algo': 'fake-hash_algo', 712 'os_hash_value': 'fake-hash_value'}) 713 finally: 714 try: 715 os.remove(f.name) 716 except Exception: 717 pass 718 719 @mock.patch('sys.stdin', autospec=True) 720 def test_do_image_create_hidden_image(self, mock_stdin): 721 args = self._make_args({'name': 'IMG-01', 'disk_format': 'vhd', 722 'container_format': 'bare', 723 'file': None, 724 'os_hidden': True}) 725 with mock.patch.object(self.gc.images, 'create') as mocked_create: 726 ignore_fields = ['self', 'access', 'file', 'schema'] 727 expect_image = dict([(field, field) for field in ignore_fields]) 728 expect_image['id'] = 'pass' 729 expect_image['name'] = 'IMG-01' 730 expect_image['disk_format'] = 'vhd' 731 expect_image['container_format'] = 'bare' 732 expect_image['os_hidden'] = True 733 mocked_create.return_value = expect_image 734 735 # Ensure that the test stdin is not considered 736 # to be supplying image data 737 mock_stdin.isatty = lambda: True 738 test_shell.do_image_create(self.gc, args) 739 740 mocked_create.assert_called_once_with(name='IMG-01', 741 disk_format='vhd', 742 container_format='bare', 743 os_hidden=True) 744 utils.print_dict.assert_called_once_with({ 745 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 746 'container_format': 'bare', 'os_hidden': True}) 747 748 def test_do_image_create_with_file(self): 749 self.mock_get_data_file.return_value = io.StringIO() 750 try: 751 file_name = None 752 with open(tempfile.mktemp(), 'w+') as f: 753 f.write('Some data here') 754 f.flush() 755 f.seek(0) 756 file_name = f.name 757 temp_args = {'name': 'IMG-01', 758 'disk_format': 'vhd', 759 'container_format': 'bare', 760 'file': file_name, 761 'progress': False} 762 args = self._make_args(temp_args) 763 with mock.patch.object(self.gc.images, 'create') as mocked_create: 764 with mock.patch.object(self.gc.images, 'get') as mocked_get: 765 766 ignore_fields = ['self', 'access', 'schema'] 767 expect_image = dict([(field, field) for field in 768 ignore_fields]) 769 expect_image['id'] = 'pass' 770 expect_image['name'] = 'IMG-01' 771 expect_image['disk_format'] = 'vhd' 772 expect_image['container_format'] = 'bare' 773 mocked_create.return_value = expect_image 774 mocked_get.return_value = expect_image 775 776 test_shell.do_image_create(self.gc, args) 777 778 temp_args.pop('file', None) 779 mocked_create.assert_called_once_with(**temp_args) 780 mocked_get.assert_called_once_with('pass') 781 utils.print_dict.assert_called_once_with({ 782 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 783 'container_format': 'bare'}) 784 finally: 785 try: 786 os.remove(f.name) 787 except Exception: 788 pass 789 790 @mock.patch('sys.stdin', autospec=True) 791 def test_do_image_create_with_unicode(self, mock_stdin): 792 name = u'\u041f\u0420\u0418\u0412\u0415\u0422\u0418\u041a' 793 794 args = self._make_args({'name': name, 795 'file': None}) 796 with mock.patch.object(self.gc.images, 'create') as mocked_create: 797 ignore_fields = ['self', 'access', 'file', 'schema'] 798 expect_image = dict((field, field) for field in ignore_fields) 799 expect_image['id'] = 'pass' 800 expect_image['name'] = name 801 mocked_create.return_value = expect_image 802 803 mock_stdin.isatty = lambda: True 804 test_shell.do_image_create(self.gc, args) 805 806 mocked_create.assert_called_once_with(name=name) 807 utils.print_dict.assert_called_once_with({ 808 'id': 'pass', 'name': name}) 809 810 @mock.patch('sys.stdin', autospec=True) 811 def test_do_image_create_with_user_props(self, mock_stdin): 812 args = self._make_args({'name': 'IMG-01', 813 'property': ['myprop=myval'], 814 'file': None, 815 'container_format': 'bare', 816 'disk_format': 'qcow2'}) 817 with mock.patch.object(self.gc.images, 'create') as mocked_create: 818 ignore_fields = ['self', 'access', 'file', 'schema'] 819 expect_image = dict([(field, field) for field in ignore_fields]) 820 expect_image['id'] = 'pass' 821 expect_image['name'] = 'IMG-01' 822 expect_image['myprop'] = 'myval' 823 mocked_create.return_value = expect_image 824 825 # Ensure that the test stdin is not considered 826 # to be supplying image data 827 mock_stdin.isatty = lambda: True 828 test_shell.do_image_create(self.gc, args) 829 830 mocked_create.assert_called_once_with(name='IMG-01', 831 myprop='myval', 832 container_format='bare', 833 disk_format='qcow2') 834 utils.print_dict.assert_called_once_with({ 835 'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'}) 836 837 @mock.patch('glanceclient.common.utils.exit') 838 @mock.patch('os.access') 839 @mock.patch('sys.stdin', autospec=True) 840 def test_neg_do_image_create_no_file_and_stdin_with_store( 841 self, mock_stdin, mock_access, mock_utils_exit): 842 expected_msg = ('--store option should only be provided with --file ' 843 'option or stdin.') 844 mock_utils_exit.side_effect = self._mock_utils_exit 845 mock_stdin.isatty = lambda: True 846 mock_access.return_value = False 847 args = self._make_args({'name': 'IMG-01', 848 'property': ['myprop=myval'], 849 'file': None, 850 'store': 'file1', 851 'container_format': 'bare', 852 'disk_format': 'qcow2'}) 853 854 try: 855 test_shell.do_image_create(self.gc, args) 856 self.fail("utils.exit should have been called") 857 except SystemExit: 858 pass 859 860 mock_utils_exit.assert_called_once_with(expected_msg) 861 862 @mock.patch('glanceclient.common.utils.exit') 863 def test_neg_do_image_create_invalid_store( 864 self, mock_utils_exit): 865 expected_msg = ("Store 'dummy' is not valid for this cloud. " 866 "Valid values can be retrieved with stores-info " 867 "command.") 868 mock_utils_exit.side_effect = self._mock_utils_exit 869 args = self._make_args({'name': 'IMG-01', 870 'property': ['myprop=myval'], 871 'file': "somefile.txt", 872 'store': 'dummy', 873 'container_format': 'bare', 874 'disk_format': 'qcow2'}) 875 876 with mock.patch.object(self.gc.images, 877 'get_stores_info') as mock_stores_info: 878 mock_stores_info.return_value = self.stores_info_response 879 try: 880 test_shell.do_image_create(self.gc, args) 881 self.fail("utils.exit should have been called") 882 except SystemExit: 883 pass 884 885 mock_utils_exit.assert_called_once_with(expected_msg) 886 887 # NOTE(rosmaita): have to explicitly set to None the declared but unused 888 # arguments (the configparser does that for us normally) 889 base_args = {'name': 'Mortimer', 890 'disk_format': 'raw', 891 'container_format': 'bare', 892 'progress': False, 893 'file': None, 894 'uri': None, 895 'import_method': None} 896 897 import_info_response = {'import-methods': { 898 'type': 'array', 899 'description': 'Import methods available.', 900 'value': ['glance-direct', 'web-download', 'copy-image']}} 901 902 def _mock_utils_exit(self, msg): 903 sys.exit(msg) 904 905 @mock.patch('glanceclient.common.utils.exit') 906 @mock.patch('os.access') 907 @mock.patch('sys.stdin', autospec=True) 908 def test_neg_image_create_via_import_no_method_with_file_and_stdin( 909 self, mock_stdin, mock_access, mock_utils_exit): 910 expected_msg = ('You cannot use both --file and stdin with the ' 911 'glance-direct import method.') 912 my_args = self.base_args.copy() 913 my_args['file'] = 'some.file' 914 args = self._make_args(my_args) 915 mock_stdin.isatty = lambda: False 916 mock_access.return_value = True 917 mock_utils_exit.side_effect = self._mock_utils_exit 918 with mock.patch.object(self.gc.images, 919 'get_import_info') as mocked_info: 920 mocked_info.return_value = self.import_info_response 921 try: 922 test_shell.do_image_create_via_import(self.gc, args) 923 self.fail("utils.exit should have been called") 924 except SystemExit: 925 pass 926 mock_utils_exit.assert_called_once_with(expected_msg) 927 928 @mock.patch('glanceclient.common.utils.exit') 929 def test_neg_image_create_via_import_copy_image( 930 self, mock_utils_exit): 931 expected_msg = ("Import method 'copy-image' cannot be used " 932 "while creating the image.") 933 mock_utils_exit.side_effect = self._mock_utils_exit 934 my_args = self.base_args.copy() 935 my_args.update( 936 {'id': 'IMG-01', 'import_method': 'copy-image'}) 937 args = self._make_args(my_args) 938 939 with mock.patch.object(self.gc.images, 940 'get_import_info') as mocked_info: 941 mocked_info.return_value = self.import_info_response 942 try: 943 test_shell.do_image_create_via_import(self.gc, args) 944 self.fail("utils.exit should have been called") 945 except SystemExit: 946 pass 947 mock_utils_exit.assert_called_once_with(expected_msg) 948 949 @mock.patch('glanceclient.common.utils.exit') 950 def test_neg_image_create_via_import_stores_all_stores_specified( 951 self, mock_utils_exit): 952 expected_msg = ('Only one of --store, --stores and --all-stores can ' 953 'be provided') 954 mock_utils_exit.side_effect = self._mock_utils_exit 955 my_args = self.base_args.copy() 956 my_args.update( 957 {'id': 'IMG-01', 'import_method': 'glance-direct', 958 'stores': 'file1,file2', 'os_all_stores': True, 959 'file': 'some.mufile', 960 'disk_format': 'raw', 961 'container_format': 'bare', 962 }) 963 args = self._make_args(my_args) 964 965 with mock.patch.object(self.gc.images, 966 'get_import_info') as mocked_info: 967 mocked_info.return_value = self.import_info_response 968 try: 969 test_shell.do_image_create_via_import(self.gc, args) 970 self.fail("utils.exit should have been called") 971 except SystemExit: 972 pass 973 mock_utils_exit.assert_called_once_with(expected_msg) 974 975 @mock.patch('glanceclient.common.utils.exit') 976 @mock.patch('sys.stdin', autospec=True) 977 def test_neg_image_create_via_import_stores_without_file( 978 self, mock_stdin, mock_utils_exit): 979 expected_msg = ('--stores option should only be provided with --file ' 980 'option or stdin for the glance-direct import method.') 981 mock_utils_exit.side_effect = self._mock_utils_exit 982 mock_stdin.isatty = lambda: True 983 my_args = self.base_args.copy() 984 my_args.update( 985 {'id': 'IMG-01', 'import_method': 'glance-direct', 986 'stores': 'file1,file2', 987 'disk_format': 'raw', 988 'container_format': 'bare', 989 }) 990 args = self._make_args(my_args) 991 992 with mock.patch.object(self.gc.images, 993 'get_import_info') as mocked_info: 994 with mock.patch.object(self.gc.images, 995 'get_stores_info') as mocked_stores_info: 996 mocked_stores_info.return_value = self.stores_info_response 997 mocked_info.return_value = self.import_info_response 998 try: 999 test_shell.do_image_create_via_import(self.gc, args) 1000 self.fail("utils.exit should have been called") 1001 except SystemExit: 1002 pass 1003 mock_utils_exit.assert_called_once_with(expected_msg) 1004 1005 @mock.patch('glanceclient.common.utils.exit') 1006 @mock.patch('sys.stdin', autospec=True) 1007 def test_neg_image_create_via_import_all_stores_without_file( 1008 self, mock_stdin, mock_utils_exit): 1009 expected_msg = ('--all-stores option should only be provided with ' 1010 '--file option or stdin for the glance-direct import ' 1011 'method.') 1012 mock_utils_exit.side_effect = self._mock_utils_exit 1013 mock_stdin.isatty = lambda: True 1014 my_args = self.base_args.copy() 1015 my_args.update( 1016 {'id': 'IMG-01', 'import_method': 'glance-direct', 1017 'os_all_stores': True, 1018 'disk_format': 'raw', 1019 'container_format': 'bare', 1020 }) 1021 args = self._make_args(my_args) 1022 1023 with mock.patch.object(self.gc.images, 1024 'get_import_info') as mocked_info: 1025 mocked_info.return_value = self.import_info_response 1026 try: 1027 test_shell.do_image_create_via_import(self.gc, args) 1028 self.fail("utils.exit should have been called") 1029 except SystemExit: 1030 pass 1031 mock_utils_exit.assert_called_once_with(expected_msg) 1032 1033 @mock.patch('glanceclient.common.utils.exit') 1034 @mock.patch('os.access') 1035 @mock.patch('sys.stdin', autospec=True) 1036 def test_neg_image_create_via_import_no_file_and_stdin_with_store( 1037 self, mock_stdin, mock_access, mock_utils_exit): 1038 expected_msg = ('--store option should only be provided with --file ' 1039 'option or stdin for the glance-direct import method.') 1040 my_args = self.base_args.copy() 1041 my_args['import_method'] = 'glance-direct' 1042 my_args['store'] = 'file1' 1043 args = self._make_args(my_args) 1044 1045 mock_stdin.isatty = lambda: True 1046 mock_access.return_value = False 1047 mock_utils_exit.side_effect = self._mock_utils_exit 1048 with mock.patch.object(self.gc.images, 1049 'get_import_info') as mocked_info: 1050 with mock.patch.object(self.gc.images, 1051 'get_stores_info') as mocked_stores_info: 1052 mocked_stores_info.return_value = self.stores_info_response 1053 mocked_info.return_value = self.import_info_response 1054 try: 1055 test_shell.do_image_create_via_import(self.gc, args) 1056 self.fail("utils.exit should have been called") 1057 except SystemExit: 1058 pass 1059 mock_utils_exit.assert_called_once_with(expected_msg) 1060 1061 @mock.patch('glanceclient.common.utils.exit') 1062 @mock.patch('sys.stdin', autospec=True) 1063 def test_neg_image_create_via_import_no_uri_with_store( 1064 self, mock_stdin, mock_utils_exit): 1065 expected_msg = ('--store option should only be provided with --uri ' 1066 'option for the web-download import method.') 1067 my_args = self.base_args.copy() 1068 my_args['import_method'] = 'web-download' 1069 my_args['store'] = 'file1' 1070 args = self._make_args(my_args) 1071 mock_utils_exit.side_effect = self._mock_utils_exit 1072 with mock.patch.object(self.gc.images, 1073 'get_import_info') as mocked_info: 1074 with mock.patch.object(self.gc.images, 1075 'get_stores_info') as mocked_stores_info: 1076 mocked_stores_info.return_value = self.stores_info_response 1077 mocked_info.return_value = self.import_info_response 1078 try: 1079 test_shell.do_image_create_via_import(self.gc, args) 1080 self.fail("utils.exit should have been called") 1081 except SystemExit: 1082 pass 1083 mock_utils_exit.assert_called_once_with(expected_msg) 1084 1085 @mock.patch('glanceclient.common.utils.exit') 1086 @mock.patch('os.access') 1087 @mock.patch('sys.stdin', autospec=True) 1088 def test_neg_image_create_via_import_invalid_store( 1089 self, mock_stdin, mock_access, mock_utils_exit): 1090 expected_msg = ("Store 'dummy' is not valid for this cloud. " 1091 "Valid values can be retrieved with stores-info" 1092 " command.") 1093 my_args = self.base_args.copy() 1094 my_args['import_method'] = 'glance-direct' 1095 my_args['store'] = 'dummy' 1096 args = self._make_args(my_args) 1097 1098 mock_stdin.isatty = lambda: True 1099 mock_access.return_value = False 1100 mock_utils_exit.side_effect = self._mock_utils_exit 1101 with mock.patch.object(self.gc.images, 1102 'get_import_info') as mocked_info: 1103 with mock.patch.object(self.gc.images, 1104 'get_stores_info') as mocked_stores_info: 1105 mocked_stores_info.return_value = self.stores_info_response 1106 mocked_info.return_value = self.import_info_response 1107 try: 1108 test_shell.do_image_create_via_import(self.gc, args) 1109 self.fail("utils.exit should have been called") 1110 except SystemExit: 1111 pass 1112 mock_utils_exit.assert_called_once_with(expected_msg) 1113 1114 @mock.patch('glanceclient.common.utils.exit') 1115 @mock.patch('sys.stdin', autospec=True) 1116 def test_neg_image_create_via_import_no_method_passing_uri( 1117 self, mock_stdin, mock_utils_exit): 1118 expected_msg = ('You cannot use --uri without specifying an import ' 1119 'method.') 1120 my_args = self.base_args.copy() 1121 my_args['uri'] = 'http://example.com/whatever' 1122 args = self._make_args(my_args) 1123 mock_stdin.isatty = lambda: True 1124 mock_utils_exit.side_effect = self._mock_utils_exit 1125 with mock.patch.object(self.gc.images, 1126 'get_import_info') as mocked_info: 1127 mocked_info.return_value = self.import_info_response 1128 try: 1129 test_shell.do_image_create_via_import(self.gc, args) 1130 self.fail("utils.exit should have been called") 1131 except SystemExit: 1132 pass 1133 mock_utils_exit.assert_called_once_with(expected_msg) 1134 1135 @mock.patch('glanceclient.common.utils.exit') 1136 @mock.patch('sys.stdin', autospec=True) 1137 def test_neg_image_create_via_import_glance_direct_no_data( 1138 self, mock_stdin, mock_utils_exit): 1139 expected_msg = ('You must specify a --file or provide data via stdin ' 1140 'for the glance-direct import method.') 1141 my_args = self.base_args.copy() 1142 my_args['import_method'] = 'glance-direct' 1143 args = self._make_args(my_args) 1144 mock_stdin.isatty = lambda: True 1145 mock_utils_exit.side_effect = self._mock_utils_exit 1146 with mock.patch.object(self.gc.images, 1147 'get_import_info') as mocked_info: 1148 mocked_info.return_value = self.import_info_response 1149 try: 1150 test_shell.do_image_create_via_import(self.gc, args) 1151 self.fail("utils.exit should have been called") 1152 except SystemExit: 1153 pass 1154 mock_utils_exit.assert_called_once_with(expected_msg) 1155 1156 @mock.patch('glanceclient.common.utils.exit') 1157 @mock.patch('sys.stdin', autospec=True) 1158 def test_neg_image_create_via_import_glance_direct_with_uri( 1159 self, mock_stdin, mock_utils_exit): 1160 expected_msg = ('You cannot specify a --uri with the glance-direct ' 1161 'import method.') 1162 my_args = self.base_args.copy() 1163 my_args['import_method'] = 'glance-direct' 1164 my_args['uri'] = 'https://example.com/some/stuff' 1165 args = self._make_args(my_args) 1166 mock_stdin.isatty = lambda: True 1167 mock_utils_exit.side_effect = self._mock_utils_exit 1168 with mock.patch.object(self.gc.images, 1169 'get_import_info') as mocked_info: 1170 mocked_info.return_value = self.import_info_response 1171 try: 1172 test_shell.do_image_create_via_import(self.gc, args) 1173 self.fail("utils.exit should have been called") 1174 except SystemExit: 1175 pass 1176 mock_utils_exit.assert_called_once_with(expected_msg) 1177 1178 @mock.patch('glanceclient.common.utils.exit') 1179 @mock.patch('os.access') 1180 @mock.patch('sys.stdin', autospec=True) 1181 def test_neg_image_create_via_import_glance_direct_with_file_and_uri( 1182 self, mock_stdin, mock_access, mock_utils_exit): 1183 expected_msg = ('You cannot specify a --uri with the glance-direct ' 1184 'import method.') 1185 my_args = self.base_args.copy() 1186 my_args['import_method'] = 'glance-direct' 1187 my_args['uri'] = 'https://example.com/some/stuff' 1188 my_args['file'] = 'my.browncow' 1189 args = self._make_args(my_args) 1190 mock_stdin.isatty = lambda: True 1191 mock_access.return_value = True 1192 mock_utils_exit.side_effect = self._mock_utils_exit 1193 with mock.patch.object(self.gc.images, 1194 'get_import_info') as mocked_info: 1195 mocked_info.return_value = self.import_info_response 1196 try: 1197 test_shell.do_image_create_via_import(self.gc, args) 1198 self.fail("utils.exit should have been called") 1199 except SystemExit: 1200 pass 1201 mock_utils_exit.assert_called_once_with(expected_msg) 1202 1203 @mock.patch('glanceclient.common.utils.exit') 1204 @mock.patch('sys.stdin', autospec=True) 1205 def test_neg_image_create_via_import_glance_direct_with_data_and_uri( 1206 self, mock_stdin, mock_utils_exit): 1207 expected_msg = ('You cannot specify a --uri with the glance-direct ' 1208 'import method.') 1209 my_args = self.base_args.copy() 1210 my_args['import_method'] = 'glance-direct' 1211 my_args['uri'] = 'https://example.com/some/stuff' 1212 args = self._make_args(my_args) 1213 mock_stdin.isatty = lambda: False 1214 mock_utils_exit.side_effect = self._mock_utils_exit 1215 with mock.patch.object(self.gc.images, 1216 'get_import_info') as mocked_info: 1217 mocked_info.return_value = self.import_info_response 1218 try: 1219 test_shell.do_image_create_via_import(self.gc, args) 1220 self.fail("utils.exit should have been called") 1221 except SystemExit: 1222 pass 1223 mock_utils_exit.assert_called_once_with(expected_msg) 1224 1225 @mock.patch('glanceclient.common.utils.exit') 1226 @mock.patch('sys.stdin', autospec=True) 1227 def test_neg_image_create_via_import_web_download_no_uri( 1228 self, mock_stdin, mock_utils_exit): 1229 expected_msg = ('URI is required for web-download import method. ' 1230 'Please use \'--uri <uri>\'.') 1231 my_args = self.base_args.copy() 1232 my_args['import_method'] = 'web-download' 1233 args = self._make_args(my_args) 1234 mock_stdin.isatty = lambda: True 1235 mock_utils_exit.side_effect = self._mock_utils_exit 1236 with mock.patch.object(self.gc.images, 1237 'get_import_info') as mocked_info: 1238 mocked_info.return_value = self.import_info_response 1239 try: 1240 test_shell.do_image_create_via_import(self.gc, args) 1241 self.fail("utils.exit should have been called") 1242 except SystemExit: 1243 pass 1244 mock_utils_exit.assert_called_once_with(expected_msg) 1245 1246 @mock.patch('glanceclient.common.utils.exit') 1247 def test_neg_image_create_via_import_stores_without_uri( 1248 self, mock_utils_exit): 1249 expected_msg = ('--stores option should only be provided with --uri ' 1250 'option for the web-download import method.') 1251 mock_utils_exit.side_effect = self._mock_utils_exit 1252 my_args = self.base_args.copy() 1253 my_args.update( 1254 {'id': 'IMG-01', 'import_method': 'web-download', 1255 'stores': 'file1,file2', 1256 'disk_format': 'raw', 1257 'container_format': 'bare', 1258 }) 1259 args = self._make_args(my_args) 1260 1261 with mock.patch.object(self.gc.images, 1262 'get_import_info') as mocked_info: 1263 with mock.patch.object(self.gc.images, 1264 'get_stores_info') as mocked_stores_info: 1265 mocked_stores_info.return_value = self.stores_info_response 1266 mocked_info.return_value = self.import_info_response 1267 try: 1268 test_shell.do_image_create_via_import(self.gc, args) 1269 self.fail("utils.exit should have been called") 1270 except SystemExit: 1271 pass 1272 mock_utils_exit.assert_called_once_with(expected_msg) 1273 1274 @mock.patch('glanceclient.common.utils.exit') 1275 def test_neg_image_create_via_import_all_stores_without_uri( 1276 self, mock_utils_exit): 1277 expected_msg = ('--all-stores option should only be provided with ' 1278 '--uri option for the web-download import ' 1279 'method.') 1280 mock_utils_exit.side_effect = self._mock_utils_exit 1281 my_args = self.base_args.copy() 1282 my_args.update( 1283 {'id': 'IMG-01', 'import_method': 'web-download', 1284 'os_all_stores': True, 1285 'disk_format': 'raw', 1286 'container_format': 'bare', 1287 }) 1288 args = self._make_args(my_args) 1289 1290 with mock.patch.object(self.gc.images, 1291 'get_import_info') as mocked_info: 1292 mocked_info.return_value = self.import_info_response 1293 try: 1294 test_shell.do_image_create_via_import(self.gc, args) 1295 self.fail("utils.exit should have been called") 1296 except SystemExit: 1297 pass 1298 mock_utils_exit.assert_called_once_with(expected_msg) 1299 1300 @mock.patch('glanceclient.common.utils.exit') 1301 @mock.patch('sys.stdin', autospec=True) 1302 def test_neg_image_create_via_import_web_download_no_uri_with_file( 1303 self, mock_stdin, mock_utils_exit): 1304 expected_msg = ('URI is required for web-download import method. ' 1305 'Please use \'--uri <uri>\'.') 1306 my_args = self.base_args.copy() 1307 my_args['import_method'] = 'web-download' 1308 my_args['file'] = 'my.browncow' 1309 args = self._make_args(my_args) 1310 mock_stdin.isatty = lambda: True 1311 mock_utils_exit.side_effect = self._mock_utils_exit 1312 with mock.patch.object(self.gc.images, 1313 'get_import_info') as mocked_info: 1314 mocked_info.return_value = self.import_info_response 1315 try: 1316 test_shell.do_image_create_via_import(self.gc, args) 1317 self.fail("utils.exit should have been called") 1318 except SystemExit: 1319 pass 1320 mock_utils_exit.assert_called_once_with(expected_msg) 1321 1322 @mock.patch('glanceclient.common.utils.exit') 1323 @mock.patch('sys.stdin', autospec=True) 1324 def test_neg_image_create_via_import_web_download_no_uri_with_data( 1325 self, mock_stdin, mock_utils_exit): 1326 expected_msg = ('URI is required for web-download import method. ' 1327 'Please use \'--uri <uri>\'.') 1328 my_args = self.base_args.copy() 1329 my_args['import_method'] = 'web-download' 1330 my_args['file'] = 'my.browncow' 1331 args = self._make_args(my_args) 1332 mock_stdin.isatty = lambda: False 1333 mock_utils_exit.side_effect = self._mock_utils_exit 1334 with mock.patch.object(self.gc.images, 1335 'get_import_info') as mocked_info: 1336 mocked_info.return_value = self.import_info_response 1337 try: 1338 test_shell.do_image_create_via_import(self.gc, args) 1339 self.fail("utils.exit should have been called") 1340 except SystemExit: 1341 pass 1342 mock_utils_exit.assert_called_once_with(expected_msg) 1343 1344 @mock.patch('glanceclient.common.utils.exit') 1345 @mock.patch('sys.stdin', autospec=True) 1346 def test_neg_image_create_via_import_web_download_with_data_and_uri( 1347 self, mock_stdin, mock_utils_exit): 1348 expected_msg = ('You cannot pass data via stdin with the web-download ' 1349 'import method.') 1350 my_args = self.base_args.copy() 1351 my_args['import_method'] = 'web-download' 1352 my_args['uri'] = 'https://example.com/some/stuff' 1353 args = self._make_args(my_args) 1354 mock_stdin.isatty = lambda: False 1355 mock_utils_exit.side_effect = self._mock_utils_exit 1356 with mock.patch.object(self.gc.images, 1357 'get_import_info') as mocked_info: 1358 mocked_info.return_value = self.import_info_response 1359 try: 1360 test_shell.do_image_create_via_import(self.gc, args) 1361 self.fail("utils.exit should have been called") 1362 except SystemExit: 1363 pass 1364 mock_utils_exit.assert_called_once_with(expected_msg) 1365 1366 @mock.patch('glanceclient.common.utils.exit') 1367 @mock.patch('sys.stdin', autospec=True) 1368 def test_neg_image_create_via_import_web_download_with_file_and_uri( 1369 self, mock_stdin, mock_utils_exit): 1370 expected_msg = ('You cannot specify a --file with the web-download ' 1371 'import method.') 1372 my_args = self.base_args.copy() 1373 my_args['import_method'] = 'web-download' 1374 my_args['uri'] = 'https://example.com/some/stuff' 1375 my_args['file'] = 'my.browncow' 1376 args = self._make_args(my_args) 1377 mock_stdin.isatty = lambda: True 1378 mock_utils_exit.side_effect = self._mock_utils_exit 1379 with mock.patch.object(self.gc.images, 1380 'get_import_info') as mocked_info: 1381 mocked_info.return_value = self.import_info_response 1382 try: 1383 test_shell.do_image_create_via_import(self.gc, args) 1384 self.fail("utils.exit should have been called") 1385 except SystemExit: 1386 pass 1387 mock_utils_exit.assert_called_once_with(expected_msg) 1388 1389 @mock.patch('glanceclient.common.utils.exit') 1390 @mock.patch('sys.stdin', autospec=True) 1391 def test_neg_image_create_via_import_bad_method( 1392 self, mock_stdin, mock_utils_exit): 1393 expected_msg = ('Import method \'swift-party-time\' is not valid ' 1394 'for this cloud. Valid values can be retrieved with ' 1395 'import-info command.') 1396 my_args = self.base_args.copy() 1397 my_args['import_method'] = 'swift-party-time' 1398 args = self._make_args(my_args) 1399 mock_stdin.isatty = lambda: True 1400 mock_utils_exit.side_effect = self._mock_utils_exit 1401 with mock.patch.object(self.gc.images, 1402 'get_import_info') as mocked_info: 1403 mocked_info.return_value = self.import_info_response 1404 try: 1405 test_shell.do_image_create_via_import(self.gc, args) 1406 self.fail("utils.exit should have been called") 1407 except SystemExit: 1408 pass 1409 mock_utils_exit.assert_called_once_with(expected_msg) 1410 1411 @mock.patch('glanceclient.common.utils.exit') 1412 @mock.patch('sys.stdin', autospec=True) 1413 def test_neg_image_create_via_import_no_method_with_data_and_method_NA( 1414 self, mock_stdin, mock_utils_exit): 1415 expected_msg = ('Import method \'glance-direct\' is not valid ' 1416 'for this cloud. Valid values can be retrieved with ' 1417 'import-info command.') 1418 args = self._make_args(self.base_args) 1419 # need to fake some data, or this is "just like" a 1420 # create-image-record-only call 1421 mock_stdin.isatty = lambda: False 1422 mock_utils_exit.side_effect = self._mock_utils_exit 1423 my_import_info_response = deepcopy(self.import_info_response) 1424 my_import_info_response['import-methods']['value'] = ['web-download'] 1425 with mock.patch.object(self.gc.images, 1426 'get_import_info') as mocked_info: 1427 mocked_info.return_value = my_import_info_response 1428 try: 1429 test_shell.do_image_create_via_import(self.gc, args) 1430 self.fail("utils.exit should have been called") 1431 except SystemExit: 1432 pass 1433 mock_utils_exit.assert_called_once_with(expected_msg) 1434 1435 @mock.patch('glanceclient.common.utils.exit') 1436 @mock.patch('sys.stdin', autospec=True) 1437 def test_neg_image_create_via_import_good_method_not_available( 1438 self, mock_stdin, mock_utils_exit): 1439 """Make sure the good method names aren't hard coded somewhere""" 1440 expected_msg = ('Import method \'glance-direct\' is not valid for ' 1441 'this cloud. Valid values can be retrieved with ' 1442 'import-info command.') 1443 my_args = self.base_args.copy() 1444 my_args['import_method'] = 'glance-direct' 1445 args = self._make_args(my_args) 1446 mock_stdin.isatty = lambda: True 1447 mock_utils_exit.side_effect = self._mock_utils_exit 1448 my_import_info_response = deepcopy(self.import_info_response) 1449 my_import_info_response['import-methods']['value'] = ['bad-bad-method'] 1450 with mock.patch.object(self.gc.images, 1451 'get_import_info') as mocked_info: 1452 mocked_info.return_value = my_import_info_response 1453 try: 1454 test_shell.do_image_create_via_import(self.gc, args) 1455 self.fail("utils.exit should have been called") 1456 except SystemExit: 1457 pass 1458 mock_utils_exit.assert_called_once_with(expected_msg) 1459 1460 @mock.patch('glanceclient.v2.shell.do_image_import') 1461 @mock.patch('glanceclient.v2.shell.do_image_stage') 1462 @mock.patch('sys.stdin', autospec=True) 1463 def test_image_create_via_import_no_method_with_stdin( 1464 self, mock_stdin, mock_do_stage, mock_do_import): 1465 """Backward compat -> handle this like a glance-direct""" 1466 mock_stdin.isatty = lambda: False 1467 self.mock_get_data_file.return_value = io.StringIO() 1468 args = self._make_args(self.base_args) 1469 with mock.patch.object(self.gc.images, 'create') as mocked_create: 1470 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1471 with mock.patch.object(self.gc.images, 1472 'get_import_info') as mocked_info: 1473 1474 ignore_fields = ['self', 'access', 'schema'] 1475 expect_image = dict([(field, field) for field in 1476 ignore_fields]) 1477 expect_image['id'] = 'via-stdin' 1478 expect_image['name'] = 'Mortimer' 1479 expect_image['disk_format'] = 'raw' 1480 expect_image['container_format'] = 'bare' 1481 mocked_create.return_value = expect_image 1482 mocked_get.return_value = expect_image 1483 mocked_info.return_value = self.import_info_response 1484 1485 test_shell.do_image_create_via_import(self.gc, args) 1486 mocked_create.assert_called_once() 1487 mock_do_stage.assert_called_once() 1488 mock_do_import.assert_called_once() 1489 mocked_get.assert_called_with('via-stdin') 1490 utils.print_dict.assert_called_with({ 1491 'id': 'via-stdin', 'name': 'Mortimer', 1492 'disk_format': 'raw', 'container_format': 'bare'}) 1493 1494 @mock.patch('glanceclient.v2.shell.do_image_import') 1495 @mock.patch('glanceclient.v2.shell.do_image_stage') 1496 @mock.patch('os.access') 1497 @mock.patch('sys.stdin', autospec=True) 1498 def test_image_create_via_import_no_method_passing_file( 1499 self, mock_stdin, mock_access, mock_do_stage, mock_do_import): 1500 """Backward compat -> handle this like a glance-direct""" 1501 mock_stdin.isatty = lambda: True 1502 self.mock_get_data_file.return_value = io.StringIO() 1503 mock_access.return_value = True 1504 my_args = self.base_args.copy() 1505 my_args['file'] = 'fake-image-file.browncow' 1506 args = self._make_args(my_args) 1507 with mock.patch.object(self.gc.images, 'create') as mocked_create: 1508 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1509 with mock.patch.object(self.gc.images, 1510 'get_import_info') as mocked_info: 1511 1512 ignore_fields = ['self', 'access', 'schema'] 1513 expect_image = dict([(field, field) for field in 1514 ignore_fields]) 1515 expect_image['id'] = 'via-file' 1516 expect_image['name'] = 'Mortimer' 1517 expect_image['disk_format'] = 'raw' 1518 expect_image['container_format'] = 'bare' 1519 mocked_create.return_value = expect_image 1520 mocked_get.return_value = expect_image 1521 mocked_info.return_value = self.import_info_response 1522 1523 test_shell.do_image_create_via_import(self.gc, args) 1524 mocked_create.assert_called_once() 1525 mock_do_stage.assert_called_once() 1526 mock_do_import.assert_called_once() 1527 mocked_get.assert_called_with('via-file') 1528 utils.print_dict.assert_called_with({ 1529 'id': 'via-file', 'name': 'Mortimer', 1530 'disk_format': 'raw', 'container_format': 'bare'}) 1531 1532 @mock.patch('glanceclient.v2.shell.do_image_import') 1533 @mock.patch('glanceclient.v2.shell.do_image_stage') 1534 @mock.patch('sys.stdin', autospec=True) 1535 def test_do_image_create_via_import_with_no_method_no_data( 1536 self, mock_stdin, mock_do_image_stage, mock_do_image_import): 1537 """Create an image record without calling do_stage or do_import""" 1538 img_create_args = {'name': 'IMG-11', 1539 'os_architecture': 'powerpc', 1540 'id': 'watch-out-for-ossn-0075', 1541 'progress': False} 1542 client_args = {'import_method': None, 1543 'file': None, 1544 'uri': None} 1545 temp_args = img_create_args.copy() 1546 temp_args.update(client_args) 1547 args = self._make_args(temp_args) 1548 with mock.patch.object(self.gc.images, 'create') as mocked_create: 1549 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1550 with mock.patch.object(self.gc.images, 1551 'get_import_info') as mocked_info: 1552 1553 ignore_fields = ['self', 'access', 'schema'] 1554 expect_image = dict([(field, field) for field in 1555 ignore_fields]) 1556 expect_image['name'] = 'IMG-11' 1557 expect_image['id'] = 'watch-out-for-ossn-0075' 1558 expect_image['os_architecture'] = 'powerpc' 1559 mocked_create.return_value = expect_image 1560 mocked_get.return_value = expect_image 1561 mocked_info.return_value = self.import_info_response 1562 mock_stdin.isatty = lambda: True 1563 1564 test_shell.do_image_create_via_import(self.gc, args) 1565 mocked_create.assert_called_once_with(**img_create_args) 1566 mocked_get.assert_called_with('watch-out-for-ossn-0075') 1567 mock_do_image_stage.assert_not_called() 1568 mock_do_image_import.assert_not_called() 1569 utils.print_dict.assert_called_with({ 1570 'name': 'IMG-11', 'os_architecture': 'powerpc', 1571 'id': 'watch-out-for-ossn-0075'}) 1572 1573 @mock.patch('glanceclient.v2.shell.do_image_import') 1574 @mock.patch('glanceclient.v2.shell.do_image_stage') 1575 @mock.patch('sys.stdin', autospec=True) 1576 def test_do_image_create_via_import_with_web_download( 1577 self, mock_stdin, mock_do_image_stage, mock_do_image_import): 1578 temp_args = {'name': 'IMG-01', 1579 'disk_format': 'vhd', 1580 'container_format': 'bare', 1581 'uri': 'http://example.com/image.qcow', 1582 'import_method': 'web-download', 1583 'progress': False} 1584 args = self._make_args(temp_args) 1585 with mock.patch.object(self.gc.images, 'create') as mocked_create: 1586 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1587 with mock.patch.object(self.gc.images, 1588 'get_import_info') as mocked_info: 1589 1590 ignore_fields = ['self', 'access', 'schema'] 1591 expect_image = dict([(field, field) for field in 1592 ignore_fields]) 1593 expect_image['id'] = 'pass' 1594 expect_image['name'] = 'IMG-01' 1595 expect_image['disk_format'] = 'vhd' 1596 expect_image['container_format'] = 'bare' 1597 expect_image['status'] = 'queued' 1598 mocked_create.return_value = expect_image 1599 mocked_get.return_value = expect_image 1600 mocked_info.return_value = self.import_info_response 1601 mock_stdin.isatty = lambda: True 1602 1603 test_shell.do_image_create_via_import(self.gc, args) 1604 mock_do_image_stage.assert_not_called() 1605 mock_do_image_import.assert_called_once() 1606 mocked_create.assert_called_once_with(**temp_args) 1607 mocked_get.assert_called_with('pass') 1608 utils.print_dict.assert_called_with({ 1609 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 1610 'container_format': 'bare', 'status': 'queued'}) 1611 1612 @mock.patch('glanceclient.v2.shell.do_image_import') 1613 @mock.patch('glanceclient.v2.shell.do_image_stage') 1614 @mock.patch('sys.stdin', autospec=True) 1615 def test_do_image_create_via_import_with_web_download_with_stores( 1616 self, mock_stdin, mock_do_image_stage, mock_do_image_import): 1617 temp_args = {'name': 'IMG-01', 1618 'disk_format': 'vhd', 1619 'container_format': 'bare', 1620 'uri': 'http://example.com/image.qcow', 1621 'import_method': 'web-download', 1622 'progress': False, 1623 'stores': 'file1,file2'} 1624 tmp2_args = {'name': 'IMG-01', 1625 'disk_format': 'vhd', 1626 'container_format': 'bare', 1627 'uri': 'http://example.com/image.qcow', 1628 'import_method': 'web-download', 1629 'progress': False} 1630 args = self._make_args(temp_args) 1631 with mock.patch.object(self.gc.images, 'create') as mocked_create: 1632 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1633 with mock.patch.object(self.gc.images, 1634 'get_import_info') as mocked_info: 1635 with mock.patch.object(self.gc.images, 1636 'get_stores_info') as m_stores_info: 1637 1638 ignore_fields = ['self', 'access', 'schema'] 1639 expect_image = dict([(field, field) for field in 1640 ignore_fields]) 1641 expect_image['id'] = 'pass' 1642 expect_image['name'] = 'IMG-01' 1643 expect_image['disk_format'] = 'vhd' 1644 expect_image['container_format'] = 'bare' 1645 expect_image['status'] = 'queued' 1646 mocked_create.return_value = expect_image 1647 mocked_get.return_value = expect_image 1648 mocked_info.return_value = self.import_info_response 1649 m_stores_info.return_value = self.stores_info_response 1650 mock_stdin.isatty = lambda: True 1651 1652 test_shell.do_image_create_via_import(self.gc, args) 1653 mock_do_image_stage.assert_not_called() 1654 mock_do_image_import.assert_called_once() 1655 mocked_create.assert_called_once_with(**tmp2_args) 1656 mocked_get.assert_called_with('pass') 1657 utils.print_dict.assert_called_with({ 1658 'id': 'pass', 'name': 'IMG-01', 1659 'disk_format': 'vhd', 1660 'container_format': 'bare', 'status': 'queued'}) 1661 1662 def test_do_image_update_no_user_props(self): 1663 args = self._make_args({'id': 'pass', 'name': 'IMG-01', 1664 'disk_format': 'vhd', 1665 'container_format': 'bare'}) 1666 with mock.patch.object(self.gc.images, 'update') as mocked_update: 1667 ignore_fields = ['self', 'access', 'file', 'schema'] 1668 expect_image = dict([(field, field) for field in ignore_fields]) 1669 expect_image['id'] = 'pass' 1670 expect_image['name'] = 'IMG-01' 1671 expect_image['disk_format'] = 'vhd' 1672 expect_image['container_format'] = 'bare' 1673 mocked_update.return_value = expect_image 1674 1675 test_shell.do_image_update(self.gc, args) 1676 1677 mocked_update.assert_called_once_with('pass', 1678 None, 1679 name='IMG-01', 1680 disk_format='vhd', 1681 container_format='bare') 1682 utils.print_dict.assert_called_once_with({ 1683 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 1684 'container_format': 'bare'}) 1685 1686 def test_do_image_update_hide_image(self): 1687 args = self._make_args({'id': 'pass', 'os_hidden': 'true'}) 1688 with mock.patch.object(self.gc.images, 'update') as mocked_update: 1689 ignore_fields = ['self', 'access', 'file', 'schema'] 1690 expect_image = dict([(field, field) for field in ignore_fields]) 1691 expect_image['id'] = 'pass' 1692 expect_image['name'] = 'IMG-01' 1693 expect_image['disk_format'] = 'vhd' 1694 expect_image['container_format'] = 'bare' 1695 expect_image['os_hidden'] = True 1696 mocked_update.return_value = expect_image 1697 1698 test_shell.do_image_update(self.gc, args) 1699 1700 mocked_update.assert_called_once_with('pass', 1701 None, 1702 os_hidden='true') 1703 utils.print_dict.assert_called_once_with({ 1704 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 1705 'container_format': 'bare', 'os_hidden': True}) 1706 1707 def test_do_image_update_revert_hide_image(self): 1708 args = self._make_args({'id': 'pass', 'os_hidden': 'false'}) 1709 with mock.patch.object(self.gc.images, 'update') as mocked_update: 1710 ignore_fields = ['self', 'access', 'file', 'schema'] 1711 expect_image = dict([(field, field) for field in ignore_fields]) 1712 expect_image['id'] = 'pass' 1713 expect_image['name'] = 'IMG-01' 1714 expect_image['disk_format'] = 'vhd' 1715 expect_image['container_format'] = 'bare' 1716 expect_image['os_hidden'] = False 1717 mocked_update.return_value = expect_image 1718 1719 test_shell.do_image_update(self.gc, args) 1720 1721 mocked_update.assert_called_once_with('pass', 1722 None, 1723 os_hidden='false') 1724 utils.print_dict.assert_called_once_with({ 1725 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', 1726 'container_format': 'bare', 'os_hidden': False}) 1727 1728 def test_do_image_update_with_user_props(self): 1729 args = self._make_args({'id': 'pass', 'name': 'IMG-01', 1730 'property': ['myprop=myval']}) 1731 with mock.patch.object(self.gc.images, 'update') as mocked_update: 1732 ignore_fields = ['self', 'access', 'file', 'schema'] 1733 expect_image = dict([(field, field) for field in ignore_fields]) 1734 expect_image['id'] = 'pass' 1735 expect_image['name'] = 'IMG-01' 1736 expect_image['myprop'] = 'myval' 1737 mocked_update.return_value = expect_image 1738 1739 test_shell.do_image_update(self.gc, args) 1740 1741 mocked_update.assert_called_once_with('pass', 1742 None, 1743 name='IMG-01', 1744 myprop='myval') 1745 utils.print_dict.assert_called_once_with({ 1746 'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'}) 1747 1748 def test_do_image_update_with_remove_props(self): 1749 args = self._make_args({'id': 'pass', 'name': 'IMG-01', 1750 'disk_format': 'vhd', 1751 'remove-property': ['container_format']}) 1752 with mock.patch.object(self.gc.images, 'update') as mocked_update: 1753 ignore_fields = ['self', 'access', 'file', 'schema'] 1754 expect_image = dict([(field, field) for field in ignore_fields]) 1755 expect_image['id'] = 'pass' 1756 expect_image['name'] = 'IMG-01' 1757 expect_image['disk_format'] = 'vhd' 1758 1759 mocked_update.return_value = expect_image 1760 1761 test_shell.do_image_update(self.gc, args) 1762 1763 mocked_update.assert_called_once_with('pass', 1764 ['container_format'], 1765 name='IMG-01', 1766 disk_format='vhd') 1767 utils.print_dict.assert_called_once_with({ 1768 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd'}) 1769 1770 def test_do_explain(self): 1771 input = { 1772 'page_size': 18, 1773 'id': 'pass', 1774 'schemas': 'test', 1775 'model': 'test', 1776 } 1777 args = self._make_args(input) 1778 with mock.patch.object(utils, 'print_list'): 1779 test_shell.do_explain(self.gc, args) 1780 1781 self.gc.schemas.get.assert_called_once_with('test') 1782 1783 def test_do_location_add(self): 1784 gc = self.gc 1785 loc = {'url': 'http://foo.com/', 1786 'metadata': {'foo': 'bar'}, 1787 'validation_data': {'checksum': 'csum', 1788 'os_hash_algo': 'algo', 1789 'os_hash_value': 'value'}} 1790 args = {'id': 'pass', 1791 'url': loc['url'], 1792 'metadata': json.dumps(loc['metadata']), 1793 'checksum': 'csum', 1794 'hash_algo': 'algo', 1795 'hash_value': 'value'} 1796 with mock.patch.object(gc.images, 'add_location') as mocked_addloc: 1797 expect_image = {'id': 'pass', 'locations': [loc]} 1798 mocked_addloc.return_value = expect_image 1799 1800 test_shell.do_location_add(self.gc, self._make_args(args)) 1801 mocked_addloc.assert_called_once_with( 1802 'pass', loc['url'], loc['metadata'], 1803 validation_data=loc['validation_data']) 1804 utils.print_dict.assert_called_once_with(expect_image) 1805 1806 def test_do_location_delete(self): 1807 gc = self.gc 1808 loc_set = set(['http://foo/bar', 'http://spam/ham']) 1809 args = self._make_args({'id': 'pass', 'url': loc_set}) 1810 1811 with mock.patch.object(gc.images, 'delete_locations') as mocked_rmloc: 1812 test_shell.do_location_delete(self.gc, args) 1813 mocked_rmloc.assert_called_once_with('pass', loc_set) 1814 1815 def test_do_location_update(self): 1816 gc = self.gc 1817 loc = {'url': 'http://foo.com/', 'metadata': {'foo': 'bar'}} 1818 args = self._make_args({'id': 'pass', 1819 'url': loc['url'], 1820 'metadata': json.dumps(loc['metadata'])}) 1821 with mock.patch.object(gc.images, 'update_location') as mocked_modloc: 1822 expect_image = {'id': 'pass', 'locations': [loc]} 1823 mocked_modloc.return_value = expect_image 1824 1825 test_shell.do_location_update(self.gc, args) 1826 mocked_modloc.assert_called_once_with('pass', 1827 loc['url'], 1828 loc['metadata']) 1829 utils.print_dict.assert_called_once_with(expect_image) 1830 1831 def test_image_upload(self): 1832 args = self._make_args( 1833 {'id': 'IMG-01', 'file': 'test', 'size': 1024, 'progress': False}) 1834 1835 with mock.patch.object(self.gc.images, 'upload') as mocked_upload: 1836 utils.get_data_file = mock.Mock(return_value='testfile') 1837 mocked_upload.return_value = None 1838 test_shell.do_image_upload(self.gc, args) 1839 mocked_upload.assert_called_once_with('IMG-01', 'testfile', 1024, 1840 backend=None) 1841 1842 @mock.patch('glanceclient.common.utils.exit') 1843 def test_image_upload_invalid_store(self, mock_utils_exit): 1844 expected_msg = ("Store 'dummy' is not valid for this cloud. " 1845 "Valid values can be retrieved with stores-info " 1846 "command.") 1847 mock_utils_exit.side_effect = self._mock_utils_exit 1848 1849 args = self._make_args( 1850 {'id': 'IMG-01', 'file': 'test', 'size': 1024, 'progress': False, 1851 'store': 'dummy'}) 1852 1853 with mock.patch.object(self.gc.images, 1854 'get_stores_info') as mock_stores_info: 1855 mock_stores_info.return_value = self.stores_info_response 1856 try: 1857 test_shell.do_image_upload(self.gc, args) 1858 self.fail("utils.exit should have been called") 1859 except SystemExit: 1860 pass 1861 1862 mock_utils_exit.assert_called_once_with(expected_msg) 1863 1864 @mock.patch('glanceclient.common.utils.exit') 1865 def test_neg_image_import_not_available(self, mock_utils_exit): 1866 expected_msg = 'Target Glance does not support Image Import workflow' 1867 mock_utils_exit.side_effect = self._mock_utils_exit 1868 args = self._make_args( 1869 {'id': 'IMG-01', 'import_method': 'smarty-pants', 'uri': None}) 1870 with mock.patch.object(self.gc.images, 'import') as mocked_import: 1871 with mock.patch.object(self.gc.images, 1872 'get_import_info') as mocked_info: 1873 mocked_info.side_effect = exc.HTTPNotFound 1874 try: 1875 test_shell.do_image_import(self.gc, args) 1876 self.fail("utils.exit should have been called") 1877 except SystemExit: 1878 pass 1879 mock_utils_exit.assert_called_once_with(expected_msg) 1880 mocked_import.assert_not_called() 1881 1882 @mock.patch('glanceclient.common.utils.exit') 1883 def test_neg_image_import_bad_method(self, mock_utils_exit): 1884 expected_msg = ('Import method \'smarty-pants\' is not valid for this ' 1885 'cloud. Valid values can be retrieved with ' 1886 'import-info command.') 1887 mock_utils_exit.side_effect = self._mock_utils_exit 1888 args = self._make_args( 1889 {'id': 'IMG-01', 'import_method': 'smarty-pants', 'uri': None}) 1890 with mock.patch.object(self.gc.images, 1891 'get_import_info') as mocked_info: 1892 mocked_info.return_value = self.import_info_response 1893 try: 1894 test_shell.do_image_import(self.gc, args) 1895 self.fail("utils.exit should have been called") 1896 except SystemExit: 1897 pass 1898 mock_utils_exit.assert_called_once_with(expected_msg) 1899 1900 @mock.patch('glanceclient.common.utils.exit') 1901 def test_neg_image_import_no_methods_configured(self, mock_utils_exit): 1902 expected_msg = ('Import method \'glance-direct\' is not valid for ' 1903 'this cloud. Valid values can be retrieved with ' 1904 'import-info command.') 1905 mock_utils_exit.side_effect = self._mock_utils_exit 1906 args = self._make_args( 1907 {'id': 'IMG-01', 'import_method': 'glance-direct', 'uri': None}) 1908 with mock.patch.object(self.gc.images, 1909 'get_import_info') as mocked_info: 1910 mocked_info.return_value = {"import-methods": {"value": []}} 1911 try: 1912 test_shell.do_image_import(self.gc, args) 1913 self.fail("utils.exit should have been called") 1914 except SystemExit: 1915 pass 1916 mock_utils_exit.assert_called_once_with(expected_msg) 1917 1918 @mock.patch('glanceclient.common.utils.exit') 1919 def test_neg_image_import_glance_direct_image_not_uploading_status( 1920 self, mock_utils_exit): 1921 expected_msg = ('The \'glance-direct\' import method can only be ' 1922 'applied to an image in status \'uploading\'') 1923 mock_utils_exit.side_effect = self._mock_utils_exit 1924 args = self._make_args( 1925 {'id': 'IMG-01', 'import_method': 'glance-direct', 'uri': None}) 1926 with mock.patch.object(self.gc.images, 1927 'get_import_info') as mocked_info: 1928 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1929 mocked_get.return_value = {'status': 'queued', 1930 'container_format': 'bare', 1931 'disk_format': 'raw'} 1932 mocked_info.return_value = self.import_info_response 1933 try: 1934 test_shell.do_image_import(self.gc, args) 1935 self.fail("utils.exit should have been called") 1936 except SystemExit: 1937 pass 1938 mock_utils_exit.assert_called_once_with(expected_msg) 1939 1940 @mock.patch('glanceclient.common.utils.exit') 1941 def test_neg_image_import_web_download_image_not_queued_status( 1942 self, mock_utils_exit): 1943 expected_msg = ('The \'web-download\' import method can only be ' 1944 'applied to an image in status \'queued\'') 1945 mock_utils_exit.side_effect = self._mock_utils_exit 1946 args = self._make_args( 1947 {'id': 'IMG-01', 'import_method': 'web-download', 1948 'uri': 'http://joes-image-shack.com/funky.qcow2'}) 1949 with mock.patch.object(self.gc.images, 1950 'get_import_info') as mocked_info: 1951 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1952 mocked_get.return_value = {'status': 'uploading', 1953 'container_format': 'bare', 1954 'disk_format': 'raw'} 1955 mocked_info.return_value = self.import_info_response 1956 try: 1957 test_shell.do_image_import(self.gc, args) 1958 self.fail("utils.exit should have been called") 1959 except SystemExit: 1960 pass 1961 mock_utils_exit.assert_called_once_with(expected_msg) 1962 1963 @mock.patch('glanceclient.common.utils.exit') 1964 def test_neg_image_import_image_no_container_format( 1965 self, mock_utils_exit): 1966 expected_msg = ('The \'container_format\' and \'disk_format\' ' 1967 'properties must be set on an image before it can be ' 1968 'imported.') 1969 mock_utils_exit.side_effect = self._mock_utils_exit 1970 args = self._make_args( 1971 {'id': 'IMG-01', 'import_method': 'web-download', 1972 'uri': 'http://joes-image-shack.com/funky.qcow2'}) 1973 with mock.patch.object(self.gc.images, 1974 'get_import_info') as mocked_info: 1975 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1976 mocked_get.return_value = {'status': 'uploading', 1977 'disk_format': 'raw'} 1978 mocked_info.return_value = self.import_info_response 1979 try: 1980 test_shell.do_image_import(self.gc, args) 1981 self.fail("utils.exit should have been called") 1982 except SystemExit: 1983 pass 1984 mock_utils_exit.assert_called_once_with(expected_msg) 1985 1986 @mock.patch('glanceclient.common.utils.exit') 1987 def test_neg_image_import_image_no_disk_format( 1988 self, mock_utils_exit): 1989 expected_msg = ('The \'container_format\' and \'disk_format\' ' 1990 'properties must be set on an image before it can be ' 1991 'imported.') 1992 mock_utils_exit.side_effect = self._mock_utils_exit 1993 args = self._make_args( 1994 {'id': 'IMG-01', 'import_method': 'web-download', 1995 'uri': 'http://joes-image-shack.com/funky.qcow2'}) 1996 with mock.patch.object(self.gc.images, 1997 'get_import_info') as mocked_info: 1998 with mock.patch.object(self.gc.images, 'get') as mocked_get: 1999 mocked_get.return_value = {'status': 'uploading', 2000 'container_format': 'bare'} 2001 mocked_info.return_value = self.import_info_response 2002 try: 2003 test_shell.do_image_import(self.gc, args) 2004 self.fail("utils.exit should have been called") 2005 except SystemExit: 2006 pass 2007 mock_utils_exit.assert_called_once_with(expected_msg) 2008 2009 @mock.patch('glanceclient.common.utils.exit') 2010 def test_image_import_invalid_store(self, mock_utils_exit): 2011 expected_msg = ("Store 'dummy' is not valid for this cloud. " 2012 "Valid values can be retrieved with stores-info " 2013 "command.") 2014 mock_utils_exit.side_effect = self._mock_utils_exit 2015 2016 args = self._make_args( 2017 {'id': 'IMG-01', 'import_method': 'glance-direct', 'uri': None, 2018 'store': 'dummy'}) 2019 2020 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2021 with mock.patch.object(self.gc.images, 2022 'get_import_info') as mocked_info: 2023 mocked_get.return_value = {'status': 'uploading', 2024 'container_format': 'bare', 2025 'disk_format': 'raw'} 2026 with mock.patch.object(self.gc.images, 2027 'get_stores_info') as mock_stores_info: 2028 mocked_info.return_value = self.import_info_response 2029 mock_stores_info.return_value = self.stores_info_response 2030 try: 2031 test_shell.do_image_import(self.gc, args) 2032 self.fail("utils.exit should have been called") 2033 except SystemExit: 2034 pass 2035 2036 mock_utils_exit.assert_called_once_with(expected_msg) 2037 2038 def test_image_import_glance_direct(self): 2039 args = self._make_args( 2040 {'id': 'IMG-01', 'import_method': 'glance-direct', 'uri': None}) 2041 with mock.patch.object(self.gc.images, 'image_import') as mock_import: 2042 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2043 with mock.patch.object(self.gc.images, 2044 'get_import_info') as mocked_info: 2045 mocked_get.return_value = {'status': 'uploading', 2046 'container_format': 'bare', 2047 'disk_format': 'raw'} 2048 mocked_info.return_value = self.import_info_response 2049 mock_import.return_value = None 2050 test_shell.do_image_import(self.gc, args) 2051 mock_import.assert_called_once_with( 2052 'IMG-01', 'glance-direct', None, backend=None, 2053 all_stores=None, allow_failure=True, stores=None) 2054 2055 def test_image_import_web_download(self): 2056 args = self._make_args( 2057 {'id': 'IMG-01', 'uri': 'http://example.com/image.qcow', 2058 'import_method': 'web-download'}) 2059 with mock.patch.object(self.gc.images, 'image_import') as mock_import: 2060 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2061 with mock.patch.object(self.gc.images, 2062 'get_import_info') as mocked_info: 2063 mocked_get.return_value = {'status': 'queued', 2064 'container_format': 'bare', 2065 'disk_format': 'raw'} 2066 mocked_info.return_value = self.import_info_response 2067 mock_import.return_value = None 2068 test_shell.do_image_import(self.gc, args) 2069 mock_import.assert_called_once_with( 2070 'IMG-01', 'web-download', 2071 'http://example.com/image.qcow', 2072 all_stores=None, allow_failure=True, 2073 backend=None, stores=None) 2074 2075 @mock.patch('glanceclient.common.utils.print_image') 2076 def test_image_import_no_print_image(self, mocked_utils_print_image): 2077 args = self._make_args( 2078 {'id': 'IMG-02', 'uri': None, 'import_method': 'glance-direct', 2079 'from_create': True}) 2080 with mock.patch.object(self.gc.images, 'image_import') as mock_import: 2081 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2082 with mock.patch.object(self.gc.images, 2083 'get_import_info') as mocked_info: 2084 mocked_get.return_value = {'status': 'uploading', 2085 'container_format': 'bare', 2086 'disk_format': 'raw'} 2087 mocked_info.return_value = self.import_info_response 2088 mock_import.return_value = None 2089 test_shell.do_image_import(self.gc, args) 2090 mock_import.assert_called_once_with( 2091 'IMG-02', 'glance-direct', None, stores=None, 2092 all_stores=None, allow_failure=True, backend=None) 2093 mocked_utils_print_image.assert_not_called() 2094 2095 @mock.patch('glanceclient.common.utils.print_image') 2096 @mock.patch('glanceclient.v2.shell._validate_backend') 2097 def test_image_import_multiple_stores(self, mocked_utils_print_image, 2098 msvb): 2099 args = self._make_args( 2100 {'id': 'IMG-02', 'uri': None, 'import_method': 'glance-direct', 2101 'from_create': False, 'stores': 'site1,site2'}) 2102 with mock.patch.object(self.gc.images, 'image_import') as mock_import: 2103 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2104 with mock.patch.object(self.gc.images, 2105 'get_import_info') as mocked_info: 2106 mocked_get.return_value = {'status': 'uploading', 2107 'container_format': 'bare', 2108 'disk_format': 'raw'} 2109 mocked_info.return_value = self.import_info_response 2110 mock_import.return_value = None 2111 test_shell.do_image_import(self.gc, args) 2112 mock_import.assert_called_once_with( 2113 'IMG-02', 'glance-direct', None, all_stores=None, 2114 allow_failure=True, stores=['site1', 'site2'], 2115 backend=None) 2116 2117 @mock.patch('glanceclient.common.utils.print_image') 2118 @mock.patch('glanceclient.v2.shell._validate_backend') 2119 def test_image_import_copy_image(self, mocked_utils_print_image, 2120 msvb): 2121 args = self._make_args( 2122 {'id': 'IMG-02', 'uri': None, 'import_method': 'copy-image', 2123 'from_create': False, 'stores': 'file1,file2'}) 2124 with mock.patch.object(self.gc.images, 'image_import') as mock_import: 2125 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2126 with mock.patch.object(self.gc.images, 2127 'get_import_info') as mocked_info: 2128 mocked_get.return_value = {'status': 'active', 2129 'container_format': 'bare', 2130 'disk_format': 'raw'} 2131 mocked_info.return_value = self.import_info_response 2132 mock_import.return_value = None 2133 test_shell.do_image_import(self.gc, args) 2134 mock_import.assert_called_once_with( 2135 'IMG-02', 'copy-image', None, all_stores=None, 2136 allow_failure=True, stores=['file1', 'file2'], 2137 backend=None) 2138 2139 @mock.patch('glanceclient.common.utils.exit') 2140 def test_neg_image_import_copy_image_not_active( 2141 self, mock_utils_exit): 2142 expected_msg = ("The 'copy-image' import method can only be used on " 2143 "an image with status 'active'.") 2144 mock_utils_exit.side_effect = self._mock_utils_exit 2145 args = self._make_args( 2146 {'id': 'IMG-02', 'uri': None, 'import_method': 'copy-image', 2147 'disk_format': 'raw', 2148 'container_format': 'bare', 2149 'from_create': False, 'stores': 'file1,file2'}) 2150 with mock.patch.object( 2151 self.gc.images, 2152 'get_stores_info') as mocked_stores_info: 2153 with mock.patch.object(self.gc.images, 'get') as mocked_get: 2154 with mock.patch.object(self.gc.images, 2155 'get_import_info') as mocked_info: 2156 2157 mocked_stores_info.return_value = self.stores_info_response 2158 mocked_get.return_value = {'status': 'uploading', 2159 'container_format': 'bare', 2160 'disk_format': 'raw'} 2161 mocked_info.return_value = self.import_info_response 2162 try: 2163 test_shell.do_image_import(self.gc, args) 2164 self.fail("utils.exit should have been called") 2165 except SystemExit: 2166 pass 2167 mock_utils_exit.assert_called_once_with(expected_msg) 2168 2169 @mock.patch('glanceclient.common.utils.exit') 2170 def test_neg_image_import_stores_all_stores_not_specified( 2171 self, mock_utils_exit): 2172 expected_msg = ("Provide either --stores or --all-stores for " 2173 "'copy-image' import method.") 2174 mock_utils_exit.side_effect = self._mock_utils_exit 2175 my_args = self.base_args.copy() 2176 my_args.update( 2177 {'id': 'IMG-01', 'import_method': 'copy-image', 2178 'disk_format': 'raw', 2179 'container_format': 'bare', 2180 }) 2181 args = self._make_args(my_args) 2182 2183 with mock.patch.object(self.gc.images, 2184 'get_import_info') as mocked_info: 2185 mocked_info.return_value = self.import_info_response 2186 try: 2187 test_shell.do_image_import(self.gc, args) 2188 self.fail("utils.exit should have been called") 2189 except SystemExit: 2190 pass 2191 mock_utils_exit.assert_called_once_with(expected_msg) 2192 2193 def test_image_download(self): 2194 args = self._make_args( 2195 {'id': 'IMG-01', 'file': 'test', 'progress': True, 2196 'allow_md5_fallback': False}) 2197 2198 with mock.patch.object(self.gc.images, 'data') as mocked_data, \ 2199 mock.patch.object(utils, '_extract_request_id'): 2200 mocked_data.return_value = utils.RequestIdProxy( 2201 [c for c in 'abcdef']) 2202 2203 test_shell.do_image_download(self.gc, args) 2204 mocked_data.assert_called_once_with('IMG-01', 2205 allow_md5_fallback=False) 2206 2207 # check that non-default value is being passed correctly 2208 args.allow_md5_fallback = True 2209 with mock.patch.object(self.gc.images, 'data') as mocked_data, \ 2210 mock.patch.object(utils, '_extract_request_id'): 2211 mocked_data.return_value = utils.RequestIdProxy( 2212 [c for c in 'abcdef']) 2213 2214 test_shell.do_image_download(self.gc, args) 2215 mocked_data.assert_called_once_with('IMG-01', 2216 allow_md5_fallback=True) 2217 2218 @mock.patch.object(utils, 'exit') 2219 @mock.patch('sys.stdout', autospec=True) 2220 def test_image_download_no_file_arg(self, mocked_stdout, 2221 mocked_utils_exit): 2222 # Indicate that no file name was given as command line argument 2223 args = self._make_args({'id': '1234', 'file': None, 'progress': False, 2224 'allow_md5_fallback': False}) 2225 # Indicate that no file is specified for output redirection 2226 mocked_stdout.isatty = lambda: True 2227 test_shell.do_image_download(self.gc, args) 2228 mocked_utils_exit.assert_called_once_with( 2229 'No redirection or local file specified for downloaded image' 2230 ' data. Please specify a local file with --file to save' 2231 ' downloaded image or redirect output to another source.') 2232 2233 def test_do_image_delete(self): 2234 args = argparse.Namespace(id=['image1', 'image2']) 2235 with mock.patch.object(self.gc.images, 'delete') as mocked_delete: 2236 mocked_delete.return_value = 0 2237 2238 test_shell.do_image_delete(self.gc, args) 2239 self.assertEqual(2, mocked_delete.call_count) 2240 2241 def test_do_image_deactivate(self): 2242 args = argparse.Namespace(id='image1') 2243 with mock.patch.object(self.gc.images, 2244 'deactivate') as mocked_deactivate: 2245 mocked_deactivate.return_value = 0 2246 2247 test_shell.do_image_deactivate(self.gc, args) 2248 self.assertEqual(1, mocked_deactivate.call_count) 2249 2250 def test_do_image_reactivate(self): 2251 args = argparse.Namespace(id='image1') 2252 with mock.patch.object(self.gc.images, 2253 'reactivate') as mocked_reactivate: 2254 mocked_reactivate.return_value = 0 2255 2256 test_shell.do_image_reactivate(self.gc, args) 2257 self.assertEqual(1, mocked_reactivate.call_count) 2258 2259 @mock.patch.object(utils, 'exit') 2260 @mock.patch.object(utils, 'print_err') 2261 def test_do_image_delete_with_invalid_ids(self, mocked_print_err, 2262 mocked_utils_exit): 2263 args = argparse.Namespace(id=['image1', 'image2']) 2264 with mock.patch.object(self.gc.images, 'delete') as mocked_delete: 2265 mocked_delete.side_effect = exc.HTTPNotFound 2266 2267 test_shell.do_image_delete(self.gc, args) 2268 2269 self.assertEqual(2, mocked_delete.call_count) 2270 self.assertEqual(2, mocked_print_err.call_count) 2271 mocked_utils_exit.assert_called_once_with() 2272 2273 @mock.patch.object(utils, 'exit') 2274 def test_do_image_delete_from_store_not_found(self, mocked_utils_exit): 2275 args = argparse.Namespace(id='image1', store='store1') 2276 with mock.patch.object(self.gc.images, 2277 'delete_from_store') as mocked_delete: 2278 mocked_delete.side_effect = exc.HTTPNotFound 2279 2280 test_shell.do_stores_delete(self.gc, args) 2281 2282 self.assertEqual(1, mocked_delete.call_count) 2283 mocked_utils_exit.assert_called_once_with('Multi Backend support ' 2284 'is not enabled or ' 2285 'Image/store not found.') 2286 2287 def test_do_image_delete_from_store(self): 2288 args = argparse.Namespace(id='image1', store='store1') 2289 with mock.patch.object(self.gc.images, 2290 'delete_from_store') as mocked_delete: 2291 test_shell.do_stores_delete(self.gc, args) 2292 2293 mocked_delete.assert_called_once_with('store1', 2294 'image1') 2295 2296 @mock.patch.object(utils, 'exit') 2297 @mock.patch.object(utils, 'print_err') 2298 def test_do_image_delete_with_forbidden_ids(self, mocked_print_err, 2299 mocked_utils_exit): 2300 args = argparse.Namespace(id=['image1', 'image2']) 2301 with mock.patch.object(self.gc.images, 'delete') as mocked_delete: 2302 mocked_delete.side_effect = exc.HTTPForbidden 2303 2304 test_shell.do_image_delete(self.gc, args) 2305 2306 self.assertEqual(2, mocked_delete.call_count) 2307 self.assertEqual(2, mocked_print_err.call_count) 2308 mocked_utils_exit.assert_called_once_with() 2309 2310 @mock.patch.object(utils, 'exit') 2311 @mock.patch.object(utils, 'print_err') 2312 def test_do_image_delete_with_image_in_use(self, mocked_print_err, 2313 mocked_utils_exit): 2314 args = argparse.Namespace(id=['image1', 'image2']) 2315 with mock.patch.object(self.gc.images, 'delete') as mocked_delete: 2316 mocked_delete.side_effect = exc.HTTPConflict 2317 2318 test_shell.do_image_delete(self.gc, args) 2319 2320 self.assertEqual(2, mocked_delete.call_count) 2321 self.assertEqual(2, mocked_print_err.call_count) 2322 mocked_utils_exit.assert_called_once_with() 2323 2324 def test_do_image_delete_deleted(self): 2325 image_id = 'deleted-img' 2326 args = argparse.Namespace(id=[image_id]) 2327 with mock.patch.object(self.gc.images, 'delete') as mocked_delete: 2328 mocked_delete.side_effect = exc.HTTPNotFound 2329 2330 self.assert_exits_with_msg(func=test_shell.do_image_delete, 2331 func_args=args) 2332 2333 @mock.patch('sys.stdout', autospec=True) 2334 @mock.patch.object(utils, 'print_err') 2335 def test_do_image_download_with_forbidden_id(self, mocked_print_err, 2336 mocked_stdout): 2337 args = self._make_args({'id': 'IMG-01', 'file': None, 2338 'progress': False, 2339 'allow_md5_fallback': False}) 2340 mocked_stdout.isatty = lambda: False 2341 with mock.patch.object(self.gc.images, 'data') as mocked_data: 2342 mocked_data.side_effect = exc.HTTPForbidden 2343 try: 2344 test_shell.do_image_download(self.gc, args) 2345 self.fail('Exit not called') 2346 except SystemExit: 2347 pass 2348 2349 self.assertEqual(1, mocked_data.call_count) 2350 self.assertEqual(1, mocked_print_err.call_count) 2351 2352 @mock.patch('sys.stdout', autospec=True) 2353 @mock.patch.object(utils, 'print_err') 2354 def test_do_image_download_with_500(self, mocked_print_err, mocked_stdout): 2355 args = self._make_args({'id': 'IMG-01', 'file': None, 2356 'progress': False, 2357 'allow_md5_fallback': False}) 2358 mocked_stdout.isatty = lambda: False 2359 with mock.patch.object(self.gc.images, 'data') as mocked_data: 2360 mocked_data.side_effect = exc.HTTPInternalServerError 2361 try: 2362 test_shell.do_image_download(self.gc, args) 2363 self.fail('Exit not called') 2364 except SystemExit: 2365 pass 2366 2367 self.assertEqual(1, mocked_data.call_count) 2368 self.assertEqual(1, mocked_print_err.call_count) 2369 2370 def test_do_member_list(self): 2371 args = self._make_args({'image_id': 'IMG-01'}) 2372 with mock.patch.object(self.gc.image_members, 'list') as mocked_list: 2373 mocked_list.return_value = {} 2374 2375 test_shell.do_member_list(self.gc, args) 2376 2377 mocked_list.assert_called_once_with('IMG-01') 2378 columns = ['Image ID', 'Member ID', 'Status'] 2379 utils.print_list.assert_called_once_with({}, columns) 2380 2381 def test_do_member_get(self): 2382 args = self._make_args({'image_id': 'IMG-01', 'member_id': 'MEM-01'}) 2383 with mock.patch.object(self.gc.image_members, 'get') as mock_get: 2384 mock_get.return_value = {} 2385 2386 test_shell.do_member_get(self.gc, args) 2387 2388 mock_get.assert_called_once_with('IMG-01', 'MEM-01') 2389 utils.print_dict.assert_called_once_with({}) 2390 2391 def test_do_member_create(self): 2392 args = self._make_args({'image_id': 'IMG-01', 'member_id': 'MEM-01'}) 2393 with mock.patch.object(self.gc.image_members, 'create') as mock_create: 2394 mock_create.return_value = {} 2395 2396 test_shell.do_member_create(self.gc, args) 2397 2398 mock_create.assert_called_once_with('IMG-01', 'MEM-01') 2399 columns = ['Image ID', 'Member ID', 'Status'] 2400 utils.print_list.assert_called_once_with([{}], columns) 2401 2402 def test_do_member_create_with_few_arguments(self): 2403 args = self._make_args({'image_id': None, 'member_id': 'MEM-01'}) 2404 msg = 'Unable to create member. Specify image_id and member_id' 2405 2406 self.assert_exits_with_msg(func=test_shell.do_member_create, 2407 func_args=args, 2408 err_msg=msg) 2409 2410 def test_do_member_update(self): 2411 input = { 2412 'image_id': 'IMG-01', 2413 'member_id': 'MEM-01', 2414 'member_status': 'status', 2415 } 2416 args = self._make_args(input) 2417 with mock.patch.object(self.gc.image_members, 'update') as mock_update: 2418 mock_update.return_value = {} 2419 2420 test_shell.do_member_update(self.gc, args) 2421 2422 mock_update.assert_called_once_with('IMG-01', 'MEM-01', 'status') 2423 columns = ['Image ID', 'Member ID', 'Status'] 2424 utils.print_list.assert_called_once_with([{}], columns) 2425 2426 def test_do_member_update_with_few_arguments(self): 2427 input = { 2428 'image_id': 'IMG-01', 2429 'member_id': 'MEM-01', 2430 'member_status': None, 2431 } 2432 args = self._make_args(input) 2433 msg = 'Unable to update member. Specify image_id, member_id' \ 2434 ' and member_status' 2435 2436 self.assert_exits_with_msg(func=test_shell.do_member_update, 2437 func_args=args, 2438 err_msg=msg) 2439 2440 def test_do_member_delete(self): 2441 args = self._make_args({'image_id': 'IMG-01', 'member_id': 'MEM-01'}) 2442 with mock.patch.object(self.gc.image_members, 'delete') as mock_delete: 2443 test_shell.do_member_delete(self.gc, args) 2444 2445 mock_delete.assert_called_once_with('IMG-01', 'MEM-01') 2446 2447 def test_do_member_delete_with_few_arguments(self): 2448 args = self._make_args({'image_id': None, 'member_id': 'MEM-01'}) 2449 msg = 'Unable to delete member. Specify image_id and member_id' 2450 2451 self.assert_exits_with_msg(func=test_shell.do_member_delete, 2452 func_args=args, 2453 err_msg=msg) 2454 2455 def test_image_tag_update(self): 2456 args = self._make_args({'image_id': 'IMG-01', 'tag_value': 'tag01'}) 2457 with mock.patch.object(self.gc.image_tags, 'update') as mocked_update: 2458 self.gc.images.get = mock.Mock(return_value={}) 2459 mocked_update.return_value = None 2460 2461 test_shell.do_image_tag_update(self.gc, args) 2462 2463 mocked_update.assert_called_once_with('IMG-01', 'tag01') 2464 2465 def test_image_tag_update_with_few_arguments(self): 2466 args = self._make_args({'image_id': None, 'tag_value': 'tag01'}) 2467 msg = 'Unable to update tag. Specify image_id and tag_value' 2468 2469 self.assert_exits_with_msg(func=test_shell.do_image_tag_update, 2470 func_args=args, 2471 err_msg=msg) 2472 2473 def test_image_tag_delete(self): 2474 args = self._make_args({'image_id': 'IMG-01', 'tag_value': 'tag01'}) 2475 with mock.patch.object(self.gc.image_tags, 'delete') as mocked_delete: 2476 mocked_delete.return_value = None 2477 2478 test_shell.do_image_tag_delete(self.gc, args) 2479 2480 mocked_delete.assert_called_once_with('IMG-01', 'tag01') 2481 2482 def test_image_tag_delete_with_few_arguments(self): 2483 args = self._make_args({'image_id': 'IMG-01', 'tag_value': None}) 2484 msg = 'Unable to delete tag. Specify image_id and tag_value' 2485 2486 self.assert_exits_with_msg(func=test_shell.do_image_tag_delete, 2487 func_args=args, 2488 err_msg=msg) 2489 2490 def test_do_md_namespace_create(self): 2491 args = self._make_args({'namespace': 'MyNamespace', 2492 'protected': True}) 2493 with mock.patch.object(self.gc.metadefs_namespace, 2494 'create') as mocked_create: 2495 expect_namespace = { 2496 'namespace': 'MyNamespace', 2497 'protected': True 2498 } 2499 2500 mocked_create.return_value = expect_namespace 2501 2502 test_shell.do_md_namespace_create(self.gc, args) 2503 2504 mocked_create.assert_called_once_with(namespace='MyNamespace', 2505 protected=True) 2506 utils.print_dict.assert_called_once_with(expect_namespace) 2507 2508 def test_do_md_namespace_import(self): 2509 args = self._make_args({'file': 'test'}) 2510 2511 expect_namespace = { 2512 'namespace': 'MyNamespace', 2513 'protected': True 2514 } 2515 2516 with mock.patch.object(self.gc.metadefs_namespace, 2517 'create') as mocked_create: 2518 mock_read = mock.Mock(return_value=json.dumps(expect_namespace)) 2519 mock_file = mock.Mock(read=mock_read) 2520 utils.get_data_file = mock.Mock(return_value=mock_file) 2521 mocked_create.return_value = expect_namespace 2522 2523 test_shell.do_md_namespace_import(self.gc, args) 2524 2525 mocked_create.assert_called_once_with(**expect_namespace) 2526 utils.print_dict.assert_called_once_with(expect_namespace) 2527 2528 def test_do_md_namespace_import_invalid_json(self): 2529 args = self._make_args({'file': 'test'}) 2530 mock_read = mock.Mock(return_value='Invalid') 2531 mock_file = mock.Mock(read=mock_read) 2532 utils.get_data_file = mock.Mock(return_value=mock_file) 2533 2534 self.assertRaises(SystemExit, test_shell.do_md_namespace_import, 2535 self.gc, args) 2536 2537 def test_do_md_namespace_import_no_input(self): 2538 args = self._make_args({'file': None}) 2539 utils.get_data_file = mock.Mock(return_value=None) 2540 2541 self.assertRaises(SystemExit, test_shell.do_md_namespace_import, 2542 self.gc, args) 2543 2544 def test_do_md_namespace_update(self): 2545 args = self._make_args({'id': 'MyNamespace', 2546 'protected': True}) 2547 with mock.patch.object(self.gc.metadefs_namespace, 2548 'update') as mocked_update: 2549 expect_namespace = { 2550 'namespace': 'MyNamespace', 2551 'protected': True 2552 } 2553 2554 mocked_update.return_value = expect_namespace 2555 2556 test_shell.do_md_namespace_update(self.gc, args) 2557 2558 mocked_update.assert_called_once_with('MyNamespace', 2559 id='MyNamespace', 2560 protected=True) 2561 utils.print_dict.assert_called_once_with(expect_namespace) 2562 2563 def test_do_md_namespace_show(self): 2564 args = self._make_args({'namespace': 'MyNamespace', 2565 'max_column_width': 80, 2566 'resource_type': None}) 2567 with mock.patch.object(self.gc.metadefs_namespace, 2568 'get') as mocked_get: 2569 expect_namespace = {'namespace': 'MyNamespace'} 2570 2571 mocked_get.return_value = expect_namespace 2572 2573 test_shell.do_md_namespace_show(self.gc, args) 2574 2575 mocked_get.assert_called_once_with('MyNamespace') 2576 utils.print_dict.assert_called_once_with(expect_namespace, 80) 2577 2578 def test_do_md_namespace_show_resource_type(self): 2579 args = self._make_args({'namespace': 'MyNamespace', 2580 'max_column_width': 80, 2581 'resource_type': 'RESOURCE'}) 2582 with mock.patch.object(self.gc.metadefs_namespace, 2583 'get') as mocked_get: 2584 expect_namespace = {'namespace': 'MyNamespace'} 2585 2586 mocked_get.return_value = expect_namespace 2587 2588 test_shell.do_md_namespace_show(self.gc, args) 2589 2590 mocked_get.assert_called_once_with('MyNamespace', 2591 resource_type='RESOURCE') 2592 utils.print_dict.assert_called_once_with(expect_namespace, 80) 2593 2594 def test_do_md_namespace_list(self): 2595 args = self._make_args({'resource_type': None, 2596 'visibility': None, 2597 'page_size': None}) 2598 with mock.patch.object(self.gc.metadefs_namespace, 2599 'list') as mocked_list: 2600 expect_namespaces = [{'namespace': 'MyNamespace'}] 2601 2602 mocked_list.return_value = expect_namespaces 2603 2604 test_shell.do_md_namespace_list(self.gc, args) 2605 2606 mocked_list.assert_called_once_with(filters={}) 2607 utils.print_list.assert_called_once_with(expect_namespaces, 2608 ['namespace']) 2609 2610 def test_do_md_namespace_list_page_size(self): 2611 args = self._make_args({'resource_type': None, 2612 'visibility': None, 2613 'page_size': 2}) 2614 with mock.patch.object(self.gc.metadefs_namespace, 2615 'list') as mocked_list: 2616 expect_namespaces = [{'namespace': 'MyNamespace'}] 2617 2618 mocked_list.return_value = expect_namespaces 2619 2620 test_shell.do_md_namespace_list(self.gc, args) 2621 2622 mocked_list.assert_called_once_with(filters={}, page_size=2) 2623 utils.print_list.assert_called_once_with(expect_namespaces, 2624 ['namespace']) 2625 2626 def test_do_md_namespace_list_one_filter(self): 2627 args = self._make_args({'resource_types': ['OS::Compute::Aggregate'], 2628 'visibility': None, 2629 'page_size': None}) 2630 with mock.patch.object(self.gc.metadefs_namespace, 'list') as \ 2631 mocked_list: 2632 expect_namespaces = [{'namespace': 'MyNamespace'}] 2633 2634 mocked_list.return_value = expect_namespaces 2635 2636 test_shell.do_md_namespace_list(self.gc, args) 2637 2638 mocked_list.assert_called_once_with(filters={ 2639 'resource_types': ['OS::Compute::Aggregate']}) 2640 utils.print_list.assert_called_once_with(expect_namespaces, 2641 ['namespace']) 2642 2643 def test_do_md_namespace_list_all_filters(self): 2644 args = self._make_args({'resource_types': ['OS::Compute::Aggregate'], 2645 'visibility': 'public', 2646 'page_size': None}) 2647 with mock.patch.object(self.gc.metadefs_namespace, 2648 'list') as mocked_list: 2649 expect_namespaces = [{'namespace': 'MyNamespace'}] 2650 2651 mocked_list.return_value = expect_namespaces 2652 2653 test_shell.do_md_namespace_list(self.gc, args) 2654 2655 mocked_list.assert_called_once_with(filters={ 2656 'resource_types': ['OS::Compute::Aggregate'], 2657 'visibility': 'public'}) 2658 utils.print_list.assert_called_once_with(expect_namespaces, 2659 ['namespace']) 2660 2661 def test_do_md_namespace_list_unknown_filter(self): 2662 args = self._make_args({'resource_type': None, 2663 'visibility': None, 2664 'some_arg': 'some_value', 2665 'page_size': None}) 2666 with mock.patch.object(self.gc.metadefs_namespace, 2667 'list') as mocked_list: 2668 expect_namespaces = [{'namespace': 'MyNamespace'}] 2669 2670 mocked_list.return_value = expect_namespaces 2671 2672 test_shell.do_md_namespace_list(self.gc, args) 2673 2674 mocked_list.assert_called_once_with(filters={}) 2675 utils.print_list.assert_called_once_with(expect_namespaces, 2676 ['namespace']) 2677 2678 def test_do_md_namespace_delete(self): 2679 args = self._make_args({'namespace': 'MyNamespace', 2680 'content': False}) 2681 with mock.patch.object(self.gc.metadefs_namespace, 'delete') as \ 2682 mocked_delete: 2683 test_shell.do_md_namespace_delete(self.gc, args) 2684 2685 mocked_delete.assert_called_once_with('MyNamespace') 2686 2687 def test_do_md_resource_type_associate(self): 2688 args = self._make_args({'namespace': 'MyNamespace', 2689 'name': 'MyResourceType', 2690 'prefix': 'PREFIX:'}) 2691 with mock.patch.object(self.gc.metadefs_resource_type, 2692 'associate') as mocked_associate: 2693 expect_rt = { 2694 'namespace': 'MyNamespace', 2695 'name': 'MyResourceType', 2696 'prefix': 'PREFIX:' 2697 } 2698 2699 mocked_associate.return_value = expect_rt 2700 2701 test_shell.do_md_resource_type_associate(self.gc, args) 2702 2703 mocked_associate.assert_called_once_with('MyNamespace', 2704 **expect_rt) 2705 utils.print_dict.assert_called_once_with(expect_rt) 2706 2707 def test_do_md_resource_type_deassociate(self): 2708 args = self._make_args({'namespace': 'MyNamespace', 2709 'resource_type': 'MyResourceType'}) 2710 with mock.patch.object(self.gc.metadefs_resource_type, 2711 'deassociate') as mocked_deassociate: 2712 test_shell.do_md_resource_type_deassociate(self.gc, args) 2713 2714 mocked_deassociate.assert_called_once_with('MyNamespace', 2715 'MyResourceType') 2716 2717 def test_do_md_resource_type_list(self): 2718 args = self._make_args({}) 2719 with mock.patch.object(self.gc.metadefs_resource_type, 2720 'list') as mocked_list: 2721 expect_objects = ['MyResourceType1', 'MyResourceType2'] 2722 2723 mocked_list.return_value = expect_objects 2724 2725 test_shell.do_md_resource_type_list(self.gc, args) 2726 2727 self.assertEqual(1, mocked_list.call_count) 2728 2729 def test_do_md_namespace_resource_type_list(self): 2730 args = self._make_args({'namespace': 'MyNamespace'}) 2731 with mock.patch.object(self.gc.metadefs_resource_type, 2732 'get') as mocked_get: 2733 expect_objects = [{'namespace': 'MyNamespace', 2734 'object': 'MyObject'}] 2735 2736 mocked_get.return_value = expect_objects 2737 2738 test_shell.do_md_namespace_resource_type_list(self.gc, args) 2739 2740 mocked_get.assert_called_once_with('MyNamespace') 2741 utils.print_list.assert_called_once_with(expect_objects, 2742 ['name', 'prefix', 2743 'properties_target']) 2744 2745 def test_do_md_property_create(self): 2746 args = self._make_args({'namespace': 'MyNamespace', 2747 'name': "MyProperty", 2748 'title': "Title", 2749 'schema': '{}'}) 2750 with mock.patch.object(self.gc.metadefs_property, 2751 'create') as mocked_create: 2752 expect_property = { 2753 'namespace': 'MyNamespace', 2754 'name': 'MyProperty', 2755 'title': 'Title' 2756 } 2757 2758 mocked_create.return_value = expect_property 2759 2760 test_shell.do_md_property_create(self.gc, args) 2761 2762 mocked_create.assert_called_once_with('MyNamespace', 2763 name='MyProperty', 2764 title='Title') 2765 utils.print_dict.assert_called_once_with(expect_property) 2766 2767 def test_do_md_property_create_invalid_schema(self): 2768 args = self._make_args({'namespace': 'MyNamespace', 2769 'name': "MyProperty", 2770 'title': "Title", 2771 'schema': 'Invalid'}) 2772 self.assertRaises(SystemExit, test_shell.do_md_property_create, 2773 self.gc, args) 2774 2775 def test_do_md_property_update(self): 2776 args = self._make_args({'namespace': 'MyNamespace', 2777 'property': 'MyProperty', 2778 'name': 'NewName', 2779 'title': "Title", 2780 'schema': '{}'}) 2781 with mock.patch.object(self.gc.metadefs_property, 2782 'update') as mocked_update: 2783 expect_property = { 2784 'namespace': 'MyNamespace', 2785 'name': 'MyProperty', 2786 'title': 'Title' 2787 } 2788 2789 mocked_update.return_value = expect_property 2790 2791 test_shell.do_md_property_update(self.gc, args) 2792 2793 mocked_update.assert_called_once_with('MyNamespace', 'MyProperty', 2794 name='NewName', 2795 title='Title') 2796 utils.print_dict.assert_called_once_with(expect_property) 2797 2798 def test_do_md_property_update_invalid_schema(self): 2799 args = self._make_args({'namespace': 'MyNamespace', 2800 'property': 'MyProperty', 2801 'name': "MyObject", 2802 'title': "Title", 2803 'schema': 'Invalid'}) 2804 self.assertRaises(SystemExit, test_shell.do_md_property_update, 2805 self.gc, args) 2806 2807 def test_do_md_property_show(self): 2808 args = self._make_args({'namespace': 'MyNamespace', 2809 'property': 'MyProperty', 2810 'max_column_width': 80}) 2811 with mock.patch.object(self.gc.metadefs_property, 'get') as mocked_get: 2812 expect_property = { 2813 'namespace': 'MyNamespace', 2814 'property': 'MyProperty', 2815 'title': 'Title' 2816 } 2817 2818 mocked_get.return_value = expect_property 2819 2820 test_shell.do_md_property_show(self.gc, args) 2821 2822 mocked_get.assert_called_once_with('MyNamespace', 'MyProperty') 2823 utils.print_dict.assert_called_once_with(expect_property, 80) 2824 2825 def test_do_md_property_delete(self): 2826 args = self._make_args({'namespace': 'MyNamespace', 2827 'property': 'MyProperty'}) 2828 with mock.patch.object(self.gc.metadefs_property, 2829 'delete') as mocked_delete: 2830 test_shell.do_md_property_delete(self.gc, args) 2831 2832 mocked_delete.assert_called_once_with('MyNamespace', 'MyProperty') 2833 2834 def test_do_md_namespace_property_delete(self): 2835 args = self._make_args({'namespace': 'MyNamespace'}) 2836 with mock.patch.object(self.gc.metadefs_property, 2837 'delete_all') as mocked_delete_all: 2838 test_shell.do_md_namespace_properties_delete(self.gc, args) 2839 2840 mocked_delete_all.assert_called_once_with('MyNamespace') 2841 2842 def test_do_md_property_list(self): 2843 args = self._make_args({'namespace': 'MyNamespace'}) 2844 with mock.patch.object(self.gc.metadefs_property, 2845 'list') as mocked_list: 2846 expect_objects = [{'namespace': 'MyNamespace', 2847 'property': 'MyProperty', 2848 'title': 'MyTitle'}] 2849 2850 mocked_list.return_value = expect_objects 2851 2852 test_shell.do_md_property_list(self.gc, args) 2853 2854 mocked_list.assert_called_once_with('MyNamespace') 2855 utils.print_list.assert_called_once_with(expect_objects, 2856 ['name', 'title', 'type']) 2857 2858 def test_do_md_object_create(self): 2859 args = self._make_args({'namespace': 'MyNamespace', 2860 'name': "MyObject", 2861 'schema': '{}'}) 2862 with mock.patch.object(self.gc.metadefs_object, 2863 'create') as mocked_create: 2864 expect_object = { 2865 'namespace': 'MyNamespace', 2866 'name': 'MyObject' 2867 } 2868 2869 mocked_create.return_value = expect_object 2870 2871 test_shell.do_md_object_create(self.gc, args) 2872 2873 mocked_create.assert_called_once_with('MyNamespace', 2874 name='MyObject') 2875 utils.print_dict.assert_called_once_with(expect_object) 2876 2877 def test_do_md_object_create_invalid_schema(self): 2878 args = self._make_args({'namespace': 'MyNamespace', 2879 'name': "MyObject", 2880 'schema': 'Invalid'}) 2881 self.assertRaises(SystemExit, test_shell.do_md_object_create, 2882 self.gc, args) 2883 2884 def test_do_md_object_update(self): 2885 args = self._make_args({'namespace': 'MyNamespace', 2886 'object': 'MyObject', 2887 'name': 'NewName', 2888 'schema': '{}'}) 2889 with mock.patch.object(self.gc.metadefs_object, 2890 'update') as mocked_update: 2891 expect_object = { 2892 'namespace': 'MyNamespace', 2893 'name': 'MyObject' 2894 } 2895 2896 mocked_update.return_value = expect_object 2897 2898 test_shell.do_md_object_update(self.gc, args) 2899 2900 mocked_update.assert_called_once_with('MyNamespace', 'MyObject', 2901 name='NewName') 2902 utils.print_dict.assert_called_once_with(expect_object) 2903 2904 def test_do_md_object_update_invalid_schema(self): 2905 args = self._make_args({'namespace': 'MyNamespace', 2906 'object': 'MyObject', 2907 'name': "MyObject", 2908 'schema': 'Invalid'}) 2909 self.assertRaises(SystemExit, test_shell.do_md_object_update, 2910 self.gc, args) 2911 2912 def test_do_md_object_show(self): 2913 args = self._make_args({'namespace': 'MyNamespace', 2914 'object': 'MyObject', 2915 'max_column_width': 80}) 2916 with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: 2917 expect_object = { 2918 'namespace': 'MyNamespace', 2919 'object': 'MyObject' 2920 } 2921 2922 mocked_get.return_value = expect_object 2923 2924 test_shell.do_md_object_show(self.gc, args) 2925 2926 mocked_get.assert_called_once_with('MyNamespace', 'MyObject') 2927 utils.print_dict.assert_called_once_with(expect_object, 80) 2928 2929 def test_do_md_object_property_show(self): 2930 args = self._make_args({'namespace': 'MyNamespace', 2931 'object': 'MyObject', 2932 'property': 'MyProperty', 2933 'max_column_width': 80}) 2934 with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: 2935 expect_object = {'name': 'MyObject', 2936 'properties': { 2937 'MyProperty': {'type': 'string'} 2938 }} 2939 2940 mocked_get.return_value = expect_object 2941 2942 test_shell.do_md_object_property_show(self.gc, args) 2943 2944 mocked_get.assert_called_once_with('MyNamespace', 'MyObject') 2945 utils.print_dict.assert_called_once_with({'type': 'string', 2946 'name': 'MyProperty'}, 2947 80) 2948 2949 def test_do_md_object_property_show_non_existing(self): 2950 args = self._make_args({'namespace': 'MyNamespace', 2951 'object': 'MyObject', 2952 'property': 'MyProperty', 2953 'max_column_width': 80}) 2954 with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: 2955 expect_object = {'name': 'MyObject', 'properties': {}} 2956 mocked_get.return_value = expect_object 2957 2958 self.assertRaises(SystemExit, 2959 test_shell.do_md_object_property_show, 2960 self.gc, args) 2961 mocked_get.assert_called_once_with('MyNamespace', 'MyObject') 2962 2963 def test_do_md_object_delete(self): 2964 args = self._make_args({'namespace': 'MyNamespace', 2965 'object': 'MyObject'}) 2966 with mock.patch.object(self.gc.metadefs_object, 2967 'delete') as mocked_delete: 2968 test_shell.do_md_object_delete(self.gc, args) 2969 2970 mocked_delete.assert_called_once_with('MyNamespace', 'MyObject') 2971 2972 def test_do_md_namespace_objects_delete(self): 2973 args = self._make_args({'namespace': 'MyNamespace'}) 2974 with mock.patch.object(self.gc.metadefs_object, 2975 'delete_all') as mocked_delete_all: 2976 test_shell.do_md_namespace_objects_delete(self.gc, args) 2977 2978 mocked_delete_all.assert_called_once_with('MyNamespace') 2979 2980 def test_do_md_object_list(self): 2981 args = self._make_args({'namespace': 'MyNamespace'}) 2982 with mock.patch.object(self.gc.metadefs_object, 'list') as mocked_list: 2983 expect_objects = [{'namespace': 'MyNamespace', 2984 'object': 'MyObject'}] 2985 2986 mocked_list.return_value = expect_objects 2987 2988 test_shell.do_md_object_list(self.gc, args) 2989 2990 mocked_list.assert_called_once_with('MyNamespace') 2991 utils.print_list.assert_called_once_with( 2992 expect_objects, 2993 ['name', 'description'], 2994 field_settings={ 2995 'description': {'align': 'l', 'max_width': 50}}) 2996 2997 def test_do_md_tag_create(self): 2998 args = self._make_args({'namespace': 'MyNamespace', 2999 'name': 'MyTag'}) 3000 with mock.patch.object(self.gc.metadefs_tag, 3001 'create') as mocked_create: 3002 expect_tag = { 3003 'namespace': 'MyNamespace', 3004 'name': 'MyTag' 3005 } 3006 3007 mocked_create.return_value = expect_tag 3008 3009 test_shell.do_md_tag_create(self.gc, args) 3010 3011 mocked_create.assert_called_once_with('MyNamespace', 'MyTag') 3012 utils.print_dict.assert_called_once_with(expect_tag) 3013 3014 def test_do_md_tag_update(self): 3015 args = self._make_args({'namespace': 'MyNamespace', 3016 'tag': 'MyTag', 3017 'name': 'NewTag'}) 3018 with mock.patch.object(self.gc.metadefs_tag, 3019 'update') as mocked_update: 3020 expect_tag = { 3021 'namespace': 'MyNamespace', 3022 'name': 'NewTag' 3023 } 3024 3025 mocked_update.return_value = expect_tag 3026 3027 test_shell.do_md_tag_update(self.gc, args) 3028 3029 mocked_update.assert_called_once_with('MyNamespace', 'MyTag', 3030 name='NewTag') 3031 utils.print_dict.assert_called_once_with(expect_tag) 3032 3033 def test_do_md_tag_show(self): 3034 args = self._make_args({'namespace': 'MyNamespace', 3035 'tag': 'MyTag', 3036 'sort_dir': 'desc'}) 3037 with mock.patch.object(self.gc.metadefs_tag, 'get') as mocked_get: 3038 expect_tag = { 3039 'namespace': 'MyNamespace', 3040 'tag': 'MyTag' 3041 } 3042 3043 mocked_get.return_value = expect_tag 3044 3045 test_shell.do_md_tag_show(self.gc, args) 3046 3047 mocked_get.assert_called_once_with('MyNamespace', 'MyTag') 3048 utils.print_dict.assert_called_once_with(expect_tag) 3049 3050 def test_do_md_tag_delete(self): 3051 args = self._make_args({'namespace': 'MyNamespace', 3052 'tag': 'MyTag'}) 3053 with mock.patch.object(self.gc.metadefs_tag, 3054 'delete') as mocked_delete: 3055 test_shell.do_md_tag_delete(self.gc, args) 3056 3057 mocked_delete.assert_called_once_with('MyNamespace', 'MyTag') 3058 3059 def test_do_md_namespace_tags_delete(self): 3060 args = self._make_args({'namespace': 'MyNamespace'}) 3061 with mock.patch.object(self.gc.metadefs_tag, 3062 'delete_all') as mocked_delete_all: 3063 test_shell.do_md_namespace_tags_delete(self.gc, args) 3064 3065 mocked_delete_all.assert_called_once_with('MyNamespace') 3066 3067 def test_do_md_tag_list(self): 3068 args = self._make_args({'namespace': 'MyNamespace'}) 3069 with mock.patch.object(self.gc.metadefs_tag, 'list') as mocked_list: 3070 expect_tags = [{'namespace': 'MyNamespace', 3071 'tag': 'MyTag'}] 3072 3073 mocked_list.return_value = expect_tags 3074 3075 test_shell.do_md_tag_list(self.gc, args) 3076 3077 mocked_list.assert_called_once_with('MyNamespace') 3078 utils.print_list.assert_called_once_with( 3079 expect_tags, 3080 ['name'], 3081 field_settings={ 3082 'description': {'align': 'l', 'max_width': 50}}) 3083 3084 def test_do_md_tag_create_multiple(self): 3085 args = self._make_args({'namespace': 'MyNamespace', 3086 'delim': ',', 3087 'names': 'MyTag1, MyTag2'}) 3088 with mock.patch.object( 3089 self.gc.metadefs_tag, 'create_multiple') as mocked_create_tags: 3090 expect_tags = [{'tags': [{'name': 'MyTag1'}, {'name': 'MyTag2'}]}] 3091 3092 mocked_create_tags.return_value = expect_tags 3093 3094 test_shell.do_md_tag_create_multiple(self.gc, args) 3095 3096 mocked_create_tags.assert_called_once_with( 3097 'MyNamespace', tags=['MyTag1', 'MyTag2']) 3098 utils.print_list.assert_called_once_with( 3099 expect_tags, 3100 ['name'], 3101 field_settings={ 3102 'description': {'align': 'l', 'max_width': 50}}) 3103