Add support for copy-image import method
This change adds support for copy-image import method which will copy existing images into multiple stores. Change-Id: I748a8fb3dbaf9c2e4d887a2ecd4325e27a8429c4 bp: copy-image-multiple-stores
This commit is contained in:
@@ -842,7 +842,7 @@ class ShellV2Test(testtools.TestCase):
|
|||||||
import_info_response = {'import-methods': {
|
import_info_response = {'import-methods': {
|
||||||
'type': 'array',
|
'type': 'array',
|
||||||
'description': 'Import methods available.',
|
'description': 'Import methods available.',
|
||||||
'value': ['glance-direct', 'web-download']}}
|
'value': ['glance-direct', 'web-download', 'copy-image']}}
|
||||||
|
|
||||||
def _mock_utils_exit(self, msg):
|
def _mock_utils_exit(self, msg):
|
||||||
sys.exit(msg)
|
sys.exit(msg)
|
||||||
@@ -870,6 +870,27 @@ class ShellV2Test(testtools.TestCase):
|
|||||||
pass
|
pass
|
||||||
mock_utils_exit.assert_called_once_with(expected_msg)
|
mock_utils_exit.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.common.utils.exit')
|
||||||
|
def test_neg_image_create_via_import_copy_image(
|
||||||
|
self, mock_utils_exit):
|
||||||
|
expected_msg = ("Import method 'copy-image' cannot be used "
|
||||||
|
"while creating the image.")
|
||||||
|
mock_utils_exit.side_effect = self._mock_utils_exit
|
||||||
|
my_args = self.base_args.copy()
|
||||||
|
my_args.update(
|
||||||
|
{'id': 'IMG-01', 'import_method': 'copy-image'})
|
||||||
|
args = self._make_args(my_args)
|
||||||
|
|
||||||
|
with mock.patch.object(self.gc.images,
|
||||||
|
'get_import_info') as mocked_info:
|
||||||
|
mocked_info.return_value = self.import_info_response
|
||||||
|
try:
|
||||||
|
test_shell.do_image_create_via_import(self.gc, args)
|
||||||
|
self.fail("utils.exit should have been called")
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
mock_utils_exit.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
@mock.patch('glanceclient.common.utils.exit')
|
@mock.patch('glanceclient.common.utils.exit')
|
||||||
def test_neg_image_create_via_import_stores_all_stores_specified(
|
def test_neg_image_create_via_import_stores_all_stores_specified(
|
||||||
self, mock_utils_exit):
|
self, mock_utils_exit):
|
||||||
@@ -1988,6 +2009,82 @@ class ShellV2Test(testtools.TestCase):
|
|||||||
allow_failure=True, stores=['site1', 'site2'],
|
allow_failure=True, stores=['site1', 'site2'],
|
||||||
backend=None)
|
backend=None)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.common.utils.print_image')
|
||||||
|
@mock.patch('glanceclient.v2.shell._validate_backend')
|
||||||
|
def test_image_import_copy_image(self, mocked_utils_print_image,
|
||||||
|
msvb):
|
||||||
|
args = self._make_args(
|
||||||
|
{'id': 'IMG-02', 'uri': None, 'import_method': 'copy-image',
|
||||||
|
'from_create': False, 'stores': 'file1,file2'})
|
||||||
|
with mock.patch.object(self.gc.images, 'image_import') as mock_import:
|
||||||
|
with mock.patch.object(self.gc.images, 'get') as mocked_get:
|
||||||
|
with mock.patch.object(self.gc.images,
|
||||||
|
'get_import_info') as mocked_info:
|
||||||
|
mocked_get.return_value = {'status': 'active',
|
||||||
|
'container_format': 'bare',
|
||||||
|
'disk_format': 'raw'}
|
||||||
|
mocked_info.return_value = self.import_info_response
|
||||||
|
mock_import.return_value = None
|
||||||
|
test_shell.do_image_import(self.gc, args)
|
||||||
|
mock_import.assert_called_once_with(
|
||||||
|
'IMG-02', 'copy-image', None, all_stores=None,
|
||||||
|
allow_failure=True, stores=['file1', 'file2'],
|
||||||
|
backend=None)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.common.utils.exit')
|
||||||
|
def test_neg_image_import_copy_image_not_active(
|
||||||
|
self, mock_utils_exit):
|
||||||
|
expected_msg = ("The 'copy-image' import method can only be used on "
|
||||||
|
"an image with status 'active'.")
|
||||||
|
mock_utils_exit.side_effect = self._mock_utils_exit
|
||||||
|
args = self._make_args(
|
||||||
|
{'id': 'IMG-02', 'uri': None, 'import_method': 'copy-image',
|
||||||
|
'disk_format': 'raw',
|
||||||
|
'container_format': 'bare',
|
||||||
|
'from_create': False, 'stores': 'file1,file2'})
|
||||||
|
with mock.patch.object(
|
||||||
|
self.gc.images,
|
||||||
|
'get_stores_info') as mocked_stores_info:
|
||||||
|
with mock.patch.object(self.gc.images, 'get') as mocked_get:
|
||||||
|
with mock.patch.object(self.gc.images,
|
||||||
|
'get_import_info') as mocked_info:
|
||||||
|
|
||||||
|
mocked_stores_info.return_value = self.stores_info_response
|
||||||
|
mocked_get.return_value = {'status': 'uploading',
|
||||||
|
'container_format': 'bare',
|
||||||
|
'disk_format': 'raw'}
|
||||||
|
mocked_info.return_value = self.import_info_response
|
||||||
|
try:
|
||||||
|
test_shell.do_image_import(self.gc, args)
|
||||||
|
self.fail("utils.exit should have been called")
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
mock_utils_exit.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.common.utils.exit')
|
||||||
|
def test_neg_image_import_stores_all_stores_not_specified(
|
||||||
|
self, mock_utils_exit):
|
||||||
|
expected_msg = ("Provide either --stores or --all-stores for "
|
||||||
|
"'copy-image' import method.")
|
||||||
|
mock_utils_exit.side_effect = self._mock_utils_exit
|
||||||
|
my_args = self.base_args.copy()
|
||||||
|
my_args.update(
|
||||||
|
{'id': 'IMG-01', 'import_method': 'copy-image',
|
||||||
|
'disk_format': 'raw',
|
||||||
|
'container_format': 'bare',
|
||||||
|
})
|
||||||
|
args = self._make_args(my_args)
|
||||||
|
|
||||||
|
with mock.patch.object(self.gc.images,
|
||||||
|
'get_import_info') as mocked_info:
|
||||||
|
mocked_info.return_value = self.import_info_response
|
||||||
|
try:
|
||||||
|
test_shell.do_image_import(self.gc, args)
|
||||||
|
self.fail("utils.exit should have been called")
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
mock_utils_exit.assert_called_once_with(expected_msg)
|
||||||
|
|
||||||
def test_image_download(self):
|
def test_image_download(self):
|
||||||
args = self._make_args(
|
args = self._make_args(
|
||||||
{'id': 'IMG-01', 'file': 'test', 'progress': True,
|
{'id': 'IMG-01', 'file': 'test', 'progress': True,
|
||||||
|
@@ -210,6 +210,10 @@ def do_image_create_via_import(gc, args):
|
|||||||
if args.import_method is None and (file_name or using_stdin):
|
if args.import_method is None and (file_name or using_stdin):
|
||||||
args.import_method = 'glance-direct'
|
args.import_method = 'glance-direct'
|
||||||
|
|
||||||
|
if args.import_method == 'copy-image':
|
||||||
|
utils.exit("Import method 'copy-image' cannot be used "
|
||||||
|
"while creating the image.")
|
||||||
|
|
||||||
# determine whether the requested import method is valid
|
# determine whether the requested import method is valid
|
||||||
import_methods = gc.images.get_import_info().get('import-methods')
|
import_methods = gc.images.get_import_info().get('import-methods')
|
||||||
if args.import_method and args.import_method not in import_methods.get(
|
if args.import_method and args.import_method not in import_methods.get(
|
||||||
@@ -735,6 +739,10 @@ def do_image_import(gc, args):
|
|||||||
utils.exit("Import method should be 'web-download' if URI is "
|
utils.exit("Import method should be 'web-download' if URI is "
|
||||||
"provided.")
|
"provided.")
|
||||||
|
|
||||||
|
if args.import_method == 'copy-image' and not (stores or all_stores):
|
||||||
|
utils.exit("Provide either --stores or --all-stores for "
|
||||||
|
"'copy-image' import method.")
|
||||||
|
|
||||||
# check image properties
|
# check image properties
|
||||||
image = gc.images.get(args.id)
|
image = gc.images.get(args.id)
|
||||||
container_format = image.get('container_format')
|
container_format = image.get('container_format')
|
||||||
@@ -752,6 +760,10 @@ def do_image_import(gc, args):
|
|||||||
if image_status != 'queued':
|
if image_status != 'queued':
|
||||||
utils.exit("The 'web-download' import method can only be applied "
|
utils.exit("The 'web-download' import method can only be applied "
|
||||||
"to an image in status 'queued'")
|
"to an image in status 'queued'")
|
||||||
|
if args.import_method == 'copy-image':
|
||||||
|
if image_status != 'active':
|
||||||
|
utils.exit("The 'copy-image' import method can only be used on "
|
||||||
|
"an image with status 'active'.")
|
||||||
|
|
||||||
# finally, do the import
|
# finally, do the import
|
||||||
gc.images.image_import(args.id, args.import_method, args.uri,
|
gc.images.image_import(args.id, args.import_method, args.uri,
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds support for copy-image import method which will copy existing
|
||||||
|
images into multiple stores.
|
Reference in New Issue
Block a user