Make image-create-via-import fail faster

Add checks to the image-create-via-import commmand so that it provides
better user feedback and doesn't begin the import workflow unless the
input has a chance of succeeding.  Preserves backward compatibility
with the current image-create command by (1) allowing an image record
only to be created when no import-method is specified AND no data is
supplied, and (2) doing the glance-direct workflow when no import-
method is specified AND data is provided.  Also adds the ability for
the import-method to be set as an env var OS_IMAGE_IMPORT_METHOD.

Change-Id: I0a225f5471a9311217b5d90ebb5fd415c369129a
Closes-bug: #1758149
This commit is contained in:
Brian Rosmaita
2018-03-22 15:49:44 -04:00
parent 6bab2404ec
commit 79543a67ed
2 changed files with 544 additions and 35 deletions

View File

@@ -14,10 +14,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse import argparse
from copy import deepcopy
import json import json
import mock import mock
import os import os
import six import six
import sys
import tempfile import tempfile
import testtools import testtools
@@ -487,7 +489,457 @@ class ShellV2Test(testtools.TestCase):
utils.print_dict.assert_called_once_with({ utils.print_dict.assert_called_once_with({
'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'}) 'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'})
def test_do_image_create_via_import_with_web_download(self): # NOTE(rosmaita): have to explicitly set to None the declared but unused
# arguments (the configparser does that for us normally)
base_args = {'name': 'Mortimer',
'disk_format': 'raw',
'container_format': 'bare',
'progress': False,
'file': None,
'uri': None,
'import_method': None}
import_info_response = {'import-methods': {
'type': 'array',
'description': 'Import methods available.',
'value': ['glance-direct', 'web-download']}}
def _mock_utils_exit(self, msg):
sys.exit(msg)
@mock.patch('glanceclient.common.utils.exit')
@mock.patch('os.access')
@mock.patch('sys.stdin', autospec=True)
def test_neg_image_create_via_import_no_method_with_file_and_stdin(
self, mock_stdin, mock_access, mock_utils_exit):
expected_msg = ('You cannot use both --file and stdin with the '
'glance-direct import method.')
my_args = self.base_args.copy()
my_args['file'] = 'some.file'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: False
mock_access.return_value = True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_no_method_passing_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You cannot use --uri without specifying an import '
'method.')
my_args = self.base_args.copy()
my_args['uri'] = 'http://example.com/whatever'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_glance_direct_no_data(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You must specify a --file or provide data via stdin '
'for the glance-direct import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'glance-direct'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_glance_direct_with_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You cannot specify a --uri with the glance-direct '
'import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'glance-direct'
my_args['uri'] = 'https://example.com/some/stuff'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('os.access')
@mock.patch('sys.stdin', autospec=True)
def test_neg_image_create_via_import_glance_direct_with_file_and_uri(
self, mock_stdin, mock_access, mock_utils_exit):
expected_msg = ('You cannot specify a --uri with the glance-direct '
'import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'glance-direct'
my_args['uri'] = 'https://example.com/some/stuff'
my_args['file'] = 'my.browncow'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_access.return_value = True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_glance_direct_with_data_and_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You cannot specify a --uri with the glance-direct '
'import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'glance-direct'
my_args['uri'] = 'https://example.com/some/stuff'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: False
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_web_download_no_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('URI is required for web-download import method. '
'Please use \'--uri <uri>\'.')
my_args = self.base_args.copy()
my_args['import_method'] = 'web-download'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_web_download_no_uri_with_file(
self, mock_stdin, mock_utils_exit):
expected_msg = ('URI is required for web-download import method. '
'Please use \'--uri <uri>\'.')
my_args = self.base_args.copy()
my_args['import_method'] = 'web-download'
my_args['file'] = 'my.browncow'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_web_download_no_uri_with_data(
self, mock_stdin, mock_utils_exit):
expected_msg = ('URI is required for web-download import method. '
'Please use \'--uri <uri>\'.')
my_args = self.base_args.copy()
my_args['import_method'] = 'web-download'
my_args['file'] = 'my.browncow'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: False
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_web_download_with_data_and_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You cannot pass data via stdin with the web-download '
'import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'web-download'
my_args['uri'] = 'https://example.com/some/stuff'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: False
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_web_download_with_file_and_uri(
self, mock_stdin, mock_utils_exit):
expected_msg = ('You cannot specify a --file with the web-download '
'import method.')
my_args = self.base_args.copy()
my_args['import_method'] = 'web-download'
my_args['uri'] = 'https://example.com/some/stuff'
my_args['file'] = 'my.browncow'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_bad_method(
self, mock_stdin, mock_utils_exit):
expected_msg = ('Import method \'swift-party-time\' is not valid '
'for this cloud. Valid values can be retrieved with '
'import-info command.')
my_args = self.base_args.copy()
my_args['import_method'] = 'swift-party-time'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_no_method_with_data_and_method_NA(
self, mock_stdin, mock_utils_exit):
expected_msg = ('Import method \'glance-direct\' is not valid '
'for this cloud. Valid values can be retrieved with '
'import-info command.')
args = self._make_args(self.base_args)
# need to fake some data, or this is "just like" a
# create-image-record-only call
mock_stdin.isatty = lambda: False
mock_utils_exit.side_effect = self._mock_utils_exit
my_import_info_response = deepcopy(self.import_info_response)
my_import_info_response['import-methods']['value'] = ['web-download']
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
mocked_info.return_value = my_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('sys.stdin', autospec=True)
def test_neg_image_create_via_import_good_method_not_available(
self, mock_stdin, mock_utils_exit):
"""Make sure the good method names aren't hard coded somewhere"""
expected_msg = ('Import method \'glance-direct\' is not valid for '
'this cloud. Valid values can be retrieved with '
'import-info command.')
my_args = self.base_args.copy()
my_args['import_method'] = 'glance-direct'
args = self._make_args(my_args)
mock_stdin.isatty = lambda: True
mock_utils_exit.side_effect = self._mock_utils_exit
my_import_info_response = deepcopy(self.import_info_response)
my_import_info_response['import-methods']['value'] = ['bad-bad-method']
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
mocked_info.return_value = my_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.v2.shell.do_image_import')
@mock.patch('glanceclient.v2.shell.do_image_stage')
@mock.patch('sys.stdin', autospec=True)
def test_image_create_via_import_no_method_with_stdin(
self, mock_stdin, mock_do_stage, mock_do_import):
"""Backward compat -> handle this like a glance-direct"""
mock_stdin.isatty = lambda: False
self.mock_get_data_file.return_value = six.StringIO()
args = self._make_args(self.base_args)
with mock.patch.object(self.gc.images, 'create') as mocked_create:
with mock.patch.object(self.gc.images, 'get') as mocked_get:
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
ignore_fields = ['self', 'access', 'schema']
expect_image = dict([(field, field) for field in
ignore_fields])
expect_image['id'] = 'via-stdin'
expect_image['name'] = 'Mortimer'
expect_image['disk_format'] = 'raw'
expect_image['container_format'] = 'bare'
mocked_create.return_value = expect_image
mocked_get.return_value = expect_image
mocked_info.return_value = self.import_info_response
test_shell.do_image_create_via_import(self.gc, args)
mocked_create.assert_called_once()
mock_do_stage.assert_called_once()
mock_do_import.assert_called_once()
mocked_get.assert_called_with('via-stdin')
utils.print_dict.assert_called_with({
'id': 'via-stdin', 'name': 'Mortimer',
'disk_format': 'raw', 'container_format': 'bare'})
@mock.patch('glanceclient.v2.shell.do_image_import')
@mock.patch('glanceclient.v2.shell.do_image_stage')
@mock.patch('os.access')
@mock.patch('sys.stdin', autospec=True)
def test_image_create_via_import_no_method_passing_file(
self, mock_stdin, mock_access, mock_do_stage, mock_do_import):
"""Backward compat -> handle this like a glance-direct"""
mock_stdin.isatty = lambda: True
self.mock_get_data_file.return_value = six.StringIO()
mock_access.return_value = True
my_args = self.base_args.copy()
my_args['file'] = 'fake-image-file.browncow'
args = self._make_args(my_args)
with mock.patch.object(self.gc.images, 'create') as mocked_create:
with mock.patch.object(self.gc.images, 'get') as mocked_get:
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
ignore_fields = ['self', 'access', 'schema']
expect_image = dict([(field, field) for field in
ignore_fields])
expect_image['id'] = 'via-file'
expect_image['name'] = 'Mortimer'
expect_image['disk_format'] = 'raw'
expect_image['container_format'] = 'bare'
mocked_create.return_value = expect_image
mocked_get.return_value = expect_image
mocked_info.return_value = self.import_info_response
test_shell.do_image_create_via_import(self.gc, args)
mocked_create.assert_called_once()
mock_do_stage.assert_called_once()
mock_do_import.assert_called_once()
mocked_get.assert_called_with('via-file')
utils.print_dict.assert_called_with({
'id': 'via-file', 'name': 'Mortimer',
'disk_format': 'raw', 'container_format': 'bare'})
@mock.patch('glanceclient.v2.shell.do_image_import')
@mock.patch('glanceclient.v2.shell.do_image_stage')
@mock.patch('sys.stdin', autospec=True)
def test_do_image_create_via_import_with_no_method_no_data(
self, mock_stdin, mock_do_image_stage, mock_do_image_import):
"""Create an image record without calling do_stage or do_import"""
img_create_args = {'name': 'IMG-11',
'os_architecture': 'powerpc',
'id': 'watch-out-for-ossn-0075',
'progress': False}
client_args = {'import_method': None,
'file': None,
'uri': None}
temp_args = img_create_args.copy()
temp_args.update(client_args)
args = self._make_args(temp_args)
with mock.patch.object(self.gc.images, 'create') as mocked_create:
with mock.patch.object(self.gc.images, 'get') as mocked_get:
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
ignore_fields = ['self', 'access', 'schema']
expect_image = dict([(field, field) for field in
ignore_fields])
expect_image['name'] = 'IMG-11'
expect_image['id'] = 'watch-out-for-ossn-0075'
expect_image['os_architecture'] = 'powerpc'
mocked_create.return_value = expect_image
mocked_get.return_value = expect_image
mocked_info.return_value = self.import_info_response
mock_stdin.isatty = lambda: True
test_shell.do_image_create_via_import(self.gc, args)
mocked_create.assert_called_once_with(**img_create_args)
mocked_get.assert_called_with('watch-out-for-ossn-0075')
mock_do_image_stage.assert_not_called()
mock_do_image_import.assert_not_called()
utils.print_dict.assert_called_with({
'name': 'IMG-11', 'os_architecture': 'powerpc',
'id': 'watch-out-for-ossn-0075'})
@mock.patch('glanceclient.v2.shell.do_image_import')
@mock.patch('glanceclient.v2.shell.do_image_stage')
@mock.patch('sys.stdin', autospec=True)
def test_do_image_create_via_import_with_web_download(
self, mock_stdin, mock_do_image_stage, mock_do_image_import):
temp_args = {'name': 'IMG-01', temp_args = {'name': 'IMG-01',
'disk_format': 'vhd', 'disk_format': 'vhd',
'container_format': 'bare', 'container_format': 'bare',
@@ -497,6 +949,9 @@ class ShellV2Test(testtools.TestCase):
args = self._make_args(temp_args) args = self._make_args(temp_args)
with mock.patch.object(self.gc.images, 'create') as mocked_create: with mock.patch.object(self.gc.images, 'create') as mocked_create:
with mock.patch.object(self.gc.images, 'get') as mocked_get: with mock.patch.object(self.gc.images, 'get') as mocked_get:
with mock.patch.object(self.gc.images,
'get_import_info') as mocked_info:
ignore_fields = ['self', 'access', 'schema'] ignore_fields = ['self', 'access', 'schema']
expect_image = dict([(field, field) for field in expect_image = dict([(field, field) for field in
ignore_fields]) ignore_fields])
@@ -506,8 +961,12 @@ class ShellV2Test(testtools.TestCase):
expect_image['container_format'] = 'bare' expect_image['container_format'] = 'bare'
mocked_create.return_value = expect_image mocked_create.return_value = expect_image
mocked_get.return_value = expect_image mocked_get.return_value = expect_image
test_shell.do_image_create_via_import(self.gc, args) mocked_info.return_value = self.import_info_response
mock_stdin.isatty = lambda: True
test_shell.do_image_create_via_import(self.gc, args)
mock_do_image_stage.assert_not_called()
mock_do_image_import.assert_called_once()
mocked_create.assert_called_once_with(**temp_args) mocked_create.assert_called_once_with(**temp_args)
mocked_get.assert_called_with('pass') mocked_get.assert_called_with('pass')
utils.print_dict.assert_called_with({ utils.print_dict.assert_called_with({

View File

@@ -102,14 +102,34 @@ def do_image_create(gc, args):
'passed to the client via stdin.')) 'passed to the client via stdin.'))
@utils.arg('--progress', action='store_true', default=False, @utils.arg('--progress', action='store_true', default=False,
help=_('Show upload progress bar.')) help=_('Show upload progress bar.'))
@utils.arg('--import-method', metavar='<METHOD>', default='glance-direct', @utils.arg('--import-method', metavar='<METHOD>',
default=utils.env('OS_IMAGE_IMPORT_METHOD', default=None),
help=_('Import method used for Image Import workflow. ' help=_('Import method used for Image Import workflow. '
'Valid values can be retrieved with import-info command.')) 'Valid values can be retrieved with import-info command. '
'Defaults to env[OS_IMAGE_IMPORT_METHOD] or if that is '
'undefined uses \'glance-direct\' if data is provided using '
'--file or stdin. Otherwise, simply creates an image '
'record if no import-method and no data is supplied'))
@utils.arg('--uri', metavar='<IMAGE_URL>', default=None, @utils.arg('--uri', metavar='<IMAGE_URL>', default=None,
help=_('URI to download the external image.')) help=_('URI to download the external image.'))
@utils.on_data_require_fields(DATA_FIELDS) @utils.on_data_require_fields(DATA_FIELDS)
def do_image_create_via_import(gc, args): def do_image_create_via_import(gc, args):
"""EXPERIMENTAL: Create a new image via image import.""" """EXPERIMENTAL: Create a new image via image import.
Use the interoperable image import workflow to create an image. This
command is designed to be backward compatible with the current image-create
command, so its behavior is as follows:
* If an import-method is specified (either on the command line or through
the OS_IMAGE_IMPORT_METHOD environment variable, then you must provide a
data source appropriate to that method (for example, --file for
glance-direct, or --uri for web-download).
* If no import-method is specified AND you provide either a --file or
data to stdin, the command will assume you are using the 'glance-direct'
import-method and will act accordingly.
* If no import-method is specified and no data is supplied via --file or
stdin, the command will simply create an image record in 'queued' status.
"""
schema = gc.schemas.get("image") schema = gc.schemas.get("image")
_args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()]
fields = dict(filter(lambda x: x[1] is not None and fields = dict(filter(lambda x: x[1] is not None and
@@ -123,24 +143,54 @@ def do_image_create_via_import(gc, args):
fields[key] = value fields[key] = value
file_name = fields.pop('file', None) file_name = fields.pop('file', None)
using_stdin = not sys.stdin.isatty()
# special processing for backward compatibility with image-create
if args.import_method is None and (file_name or using_stdin):
args.import_method = 'glance-direct'
# determine whether the requested import method is valid
import_methods = gc.images.get_import_info().get('import-methods')
if args.import_method and args.import_method not in import_methods.get(
'value'):
utils.exit("Import method '%s' is not valid for this cloud. "
"Valid values can be retrieved with import-info command." %
args.import_method)
# make sure we have all and only correct inputs for the requested method
if args.import_method is None:
if args.uri:
utils.exit("You cannot use --uri without specifying an import "
"method.")
if args.import_method == 'glance-direct':
if args.uri:
utils.exit("You cannot specify a --uri with the glance-direct "
"import method.")
if file_name is not None and os.access(file_name, os.R_OK) is False: if file_name is not None and os.access(file_name, os.R_OK) is False:
utils.exit("File %s does not exist or user does not have read " utils.exit("File %s does not exist or user does not have read "
"privileges to it" % file_name) "privileges to it." % file_name)
import_methods = gc.images.get_import_info().get('import-methods') if file_name is not None and using_stdin:
if file_name and (not import_methods or utils.exit("You cannot use both --file and stdin with the "
'glance-direct' not in import_methods.get('value')): "glance-direct import method.")
utils.exit("No suitable import method available for direct upload, " if not file_name and not using_stdin:
"please use image-create instead.") utils.exit("You must specify a --file or provide data via stdin "
if args.import_method == 'web-download' and not args.uri: "for the glance-direct import method.")
if args.import_method == 'web-download':
if not args.uri:
utils.exit("URI is required for web-download import method. " utils.exit("URI is required for web-download import method. "
"Please use '--uri <uri>'.") "Please use '--uri <uri>'.")
if args.uri and args.import_method != 'web-download': if file_name:
utils.exit("Import method should be 'web-download' if URI is " utils.exit("You cannot specify a --file with the web-download "
"provided.") "import method.")
if using_stdin:
utils.exit("You cannot pass data via stdin with the web-download "
"import method.")
# process
image = gc.images.create(**fields) image = gc.images.create(**fields)
try: try:
args.id = image['id'] args.id = image['id']
if args.import_method:
if utils.get_data_file(args) is not None: if utils.get_data_file(args) is not None:
args.size = None args.size = None
do_image_stage(gc, args) do_image_stage(gc, args)