Merge "Add --architecture support when uploading images"
This commit is contained in:
commit
00d308fd93
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
In order to allow overcloud and deploy images to vary based on architecture
|
||||
add a ``--architecture`` option to ``openstack overcloud image upload``.
|
||||
This option will add hw_architecture to the image meta-data, which will
|
||||
then be used my nova to limit node selection to matching CPU architectures.
|
|
@ -776,18 +776,41 @@ class TestOvercloudNameScenarios(TestWithScenarios):
|
|||
dict(func=utils.overcloud_kernel,
|
||||
basename='overcloud-full',
|
||||
expected=('overcloud-full-vmlinuz', '.vmlinuz'))),
|
||||
('kernel_arch',
|
||||
dict(func=utils.overcloud_kernel,
|
||||
basename='overcloud-full',
|
||||
arch='x86_64',
|
||||
expected=('x86_64-overcloud-full-vmlinuz', '.vmlinuz'))),
|
||||
('ramdisk_default',
|
||||
dict(func=utils.overcloud_ramdisk,
|
||||
basename='overcloud-full',
|
||||
expected=('overcloud-full-initrd', '.initrd'))),
|
||||
('ramdisk_arch',
|
||||
dict(func=utils.overcloud_ramdisk,
|
||||
basename='overcloud-full',
|
||||
arch='x86_64',
|
||||
expected=('x86_64-overcloud-full-initrd', '.initrd'))),
|
||||
('image_default',
|
||||
dict(func=utils.overcloud_image,
|
||||
basename='overcloud-full',
|
||||
expected=('overcloud-full', '.qcow2'))),
|
||||
('image_arch',
|
||||
dict(func=utils.overcloud_image,
|
||||
basename='overcloud-full',
|
||||
arch='x86_64',
|
||||
expected=('x86_64-overcloud-full', '.qcow2'))),
|
||||
]
|
||||
|
||||
def test_overcloud_params(self):
|
||||
observed = self.func(self.basename)
|
||||
kwargs = dict()
|
||||
for attr in ['arch']:
|
||||
if hasattr(self, attr):
|
||||
kwargs[attr] = getattr(self, attr)
|
||||
|
||||
if kwargs:
|
||||
observed = self.func(self.basename, **kwargs)
|
||||
else:
|
||||
observed = self.func(self.basename)
|
||||
|
||||
self.assertEqual(self.expected, observed)
|
||||
|
||||
|
@ -797,12 +820,28 @@ class TestDeployNameScenarios(TestWithScenarios):
|
|||
('kernel_default',
|
||||
dict(func=utils.deploy_kernel,
|
||||
expected=('bm-deploy-kernel', '.kernel'))),
|
||||
('kernel_arch',
|
||||
dict(func=utils.deploy_kernel,
|
||||
arch='x86_64',
|
||||
expected=('x86_64-bm-deploy-kernel', '.kernel'))),
|
||||
('ramdisk_default',
|
||||
dict(func=utils.deploy_ramdisk,
|
||||
expected=('bm-deploy-ramdisk', '.initramfs'))),
|
||||
('ramdisk_arch',
|
||||
dict(func=utils.deploy_ramdisk,
|
||||
arch='x86_64',
|
||||
expected=('x86_64-bm-deploy-ramdisk', '.initramfs'))),
|
||||
]
|
||||
|
||||
def test_deploy_params(self):
|
||||
observed = self.func()
|
||||
kwargs = {}
|
||||
for attr in ['arch']:
|
||||
if hasattr(self, attr):
|
||||
kwargs[attr] = getattr(self, attr)
|
||||
|
||||
if kwargs:
|
||||
observed = self.func(**kwargs)
|
||||
else:
|
||||
observed = self.func()
|
||||
|
||||
self.assertEqual(self.expected, observed)
|
||||
|
|
|
@ -311,7 +311,59 @@ class TestUploadOvercloudImage(TestPluginV1):
|
|||
], self.app.client_manager.image.images.create.call_args_list
|
||||
)
|
||||
|
||||
@mock.patch('os.path.isfile', autospec=True)
|
||||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
def test_overcloud_create_images_with_arch_v1(self, mock_subprocess_call,
|
||||
mock_isfile):
|
||||
parsed_args = self.check_parser(self.cmd, ['--arch', 'x86_64'], [])
|
||||
mock_isfile.return_value = False
|
||||
|
||||
self.cmd._get_image = mock.Mock(return_value=None)
|
||||
self.app.client_manager.image.version = 1.0
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.app.client_manager.image.images.delete.call_count
|
||||
)
|
||||
self.assertEqual(
|
||||
5,
|
||||
self.app.client_manager.image.images.create.call_count
|
||||
)
|
||||
self.assertEqual(
|
||||
[mock.call(properties={'hw_architecture': 'x86_64'},
|
||||
data=b'IMGDATA',
|
||||
name='x86_64-overcloud-full-vmlinuz',
|
||||
disk_format='aki',
|
||||
is_public=True),
|
||||
mock.call(properties={'hw_architecture': 'x86_64'},
|
||||
data=b'IMGDATA',
|
||||
name='x86_64-overcloud-full-initrd',
|
||||
disk_format='ari',
|
||||
is_public=True),
|
||||
mock.call(properties={'hw_architecture': 'x86_64',
|
||||
'kernel_id': 10, 'ramdisk_id': 10},
|
||||
name='x86_64-overcloud-full',
|
||||
data=b'IMGDATA',
|
||||
container_format='bare',
|
||||
disk_format='qcow2',
|
||||
is_public=True),
|
||||
mock.call(properties={'hw_architecture': 'x86_64'},
|
||||
data=b'IMGDATA',
|
||||
name='x86_64-bm-deploy-kernel',
|
||||
disk_format='aki',
|
||||
is_public=True),
|
||||
mock.call(properties={'hw_architecture': 'x86_64'},
|
||||
data=b'IMGDATA',
|
||||
name='x86_64-bm-deploy-ramdisk',
|
||||
disk_format='ari',
|
||||
is_public=True)
|
||||
], self.app.client_manager.image.images.create.call_args_list
|
||||
)
|
||||
|
||||
self.assertEqual(mock_subprocess_call.call_count, 2)
|
||||
# FIXME(tonyb): this is the wrong way around
|
||||
self.assertEqual(
|
||||
mock_subprocess_call.call_args_list, [
|
||||
mock.call('sudo cp -f "./ironic-python-agent.kernel" '
|
||||
|
@ -468,6 +520,59 @@ class TestUploadOvercloudImageFull(TestPluginV1):
|
|||
'"/httpboot/agent.ramdisk"', shell=True)
|
||||
])
|
||||
|
||||
@mock.patch('os.path.isfile', autospec=True)
|
||||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
def test_overcloud_create_images_with_arch(self, mock_subprocess_call,
|
||||
mock_isfile):
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
['--whole-disk', '--arch', 'x86_64'],
|
||||
[])
|
||||
mock_isfile.return_value = False
|
||||
|
||||
self.cmd._get_image = mock.Mock(return_value=None)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.app.client_manager.image.images.delete.call_count
|
||||
)
|
||||
self.assertEqual(
|
||||
3,
|
||||
self.app.client_manager.image.images.create.call_count
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(name='x86_64-overcloud-full',
|
||||
disk_format='qcow2',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='x86_64-bm-deploy-kernel',
|
||||
disk_format='aki',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='x86_64-bm-deploy-ramdisk',
|
||||
disk_format='ari',
|
||||
container_format='bare',
|
||||
visibility='public')
|
||||
], self.app.client_manager.image.images.create.call_args_list
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(mock.ANY, hw_architecture='x86_64'),
|
||||
mock.call(mock.ANY, hw_architecture='x86_64'),
|
||||
mock.call(mock.ANY, hw_architecture='x86_64'),
|
||||
], self.app.client_manager.image.images.update.call_args_list
|
||||
)
|
||||
self.assertEqual(mock_subprocess_call.call_count, 2)
|
||||
self.assertEqual(
|
||||
mock_subprocess_call.call_args_list, [
|
||||
mock.call('sudo cp -f "./ironic-python-agent.kernel" '
|
||||
'"/httpboot/agent.kernel"', shell=True),
|
||||
mock.call('sudo cp -f "./ironic-python-agent.initramfs" '
|
||||
'"/httpboot/agent.ramdisk"', shell=True)
|
||||
])
|
||||
|
||||
@mock.patch('os.path.isfile', autospec=True)
|
||||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
def test_overcloud_create_noupdate_images(self, mock_subprocess_call,
|
||||
|
@ -527,3 +632,109 @@ class TestUploadOvercloudImageFull(TestPluginV1):
|
|||
self.app.client_manager.image.images.update.call_count
|
||||
)
|
||||
self.assertEqual(mock_subprocess_call.call_count, 2)
|
||||
|
||||
|
||||
class TestUploadOvercloudImageFullMultiArch(TestPluginV1):
|
||||
# NOTE(tonyb): Really only the id is important below, but the names make
|
||||
# reading logfiles a little nicer
|
||||
images = [
|
||||
mock.Mock(id=10, name='overcloud-full'),
|
||||
mock.Mock(id=11, name='bm-deploy-kernel'),
|
||||
mock.Mock(id=12, name='bm-deploy-initrd'),
|
||||
mock.Mock(id=13, name='ppc64le-overcloud-full'),
|
||||
mock.Mock(id=14, name='ppc64le-bm-deploy-kernel'),
|
||||
mock.Mock(id=15, name='ppc64le-bm-deploy-initrd'),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestUploadOvercloudImageFullMultiArch, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = overcloud_image.UploadOvercloudImage(self.app, None)
|
||||
self.app.client_manager.image = mock.Mock()
|
||||
self.app.client_manager.image.version = 2.0
|
||||
# NOTE(tonyb): This is a little fragile. It works because
|
||||
# GlanceV2ClientAdapter.upload_image() calls
|
||||
# self.client.images.create() and self.client.images.get() once each
|
||||
# call so this way we always create() and get() the same mocked "image"
|
||||
self.app.client_manager.image.images.create.side_effect = self.images
|
||||
self.app.client_manager.image.images.get.side_effect = self.images
|
||||
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
|
||||
self.cmd._check_file_exists = mock.Mock(return_value=True)
|
||||
|
||||
@mock.patch('os.path.isfile', autospec=True)
|
||||
@mock.patch('subprocess.check_call', autospec=True)
|
||||
def test_overcloud_create_images_with_arch(self, mock_subprocess_call,
|
||||
mock_isfile):
|
||||
mock_isfile.return_value = False
|
||||
|
||||
self.cmd._get_image = mock.Mock(return_value=None)
|
||||
mock.patch
|
||||
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
['--whole-disk'],
|
||||
[])
|
||||
self.cmd.take_action(parsed_args)
|
||||
parsed_args = self.check_parser(self.cmd,
|
||||
['--whole-disk',
|
||||
'--http-boot', '/httpboot/ppc64le',
|
||||
'--arch', 'ppc64le'],
|
||||
[])
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.app.client_manager.image.images.delete.call_count
|
||||
)
|
||||
self.assertEqual(
|
||||
6,
|
||||
self.app.client_manager.image.images.create.call_count
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(name='overcloud-full',
|
||||
disk_format='qcow2',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='bm-deploy-kernel',
|
||||
disk_format='aki',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='bm-deploy-ramdisk',
|
||||
disk_format='ari',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='ppc64le-overcloud-full',
|
||||
disk_format='qcow2',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='ppc64le-bm-deploy-kernel',
|
||||
disk_format='aki',
|
||||
container_format='bare',
|
||||
visibility='public'),
|
||||
mock.call(name='ppc64le-bm-deploy-ramdisk',
|
||||
disk_format='ari',
|
||||
container_format='bare',
|
||||
visibility='public')
|
||||
], self.app.client_manager.image.images.create.call_args_list
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[mock.call(13, hw_architecture='ppc64le'),
|
||||
mock.call(14, hw_architecture='ppc64le'),
|
||||
mock.call(15, hw_architecture='ppc64le'),
|
||||
], self.app.client_manager.image.images.update.call_args_list
|
||||
)
|
||||
self.assertEqual(mock_subprocess_call.call_count, 4)
|
||||
# FIXME(tonyb): this is the wrong way around
|
||||
self.assertEqual(
|
||||
mock_subprocess_call.call_args_list, [
|
||||
mock.call('sudo cp -f "./ironic-python-agent.kernel" '
|
||||
'"/httpboot/agent.kernel"', shell=True),
|
||||
mock.call('sudo cp -f "./ironic-python-agent.initramfs" '
|
||||
'"/httpboot/agent.ramdisk"', shell=True),
|
||||
mock.call('sudo cp -f "./ironic-python-agent.kernel" '
|
||||
'"/httpboot/ppc64le/agent.kernel"', shell=True),
|
||||
mock.call('sudo cp -f "./ironic-python-agent.initramfs" '
|
||||
'"/httpboot/ppc64le/agent.ramdisk"', shell=True),
|
||||
])
|
||||
|
|
|
@ -1186,26 +1186,32 @@ def configure_logging(log, level, log_file):
|
|||
log.addHandler(fhandler)
|
||||
|
||||
|
||||
def overcloud_kernel(basename):
|
||||
return ('%s-vmlinuz' % basename,
|
||||
def _name_helper(basename, arch=None):
|
||||
if arch:
|
||||
basename = arch + '-' + basename
|
||||
return basename
|
||||
|
||||
|
||||
def overcloud_kernel(basename, arch=None):
|
||||
return (_name_helper('%s-vmlinuz' % basename, arch=arch),
|
||||
'.vmlinuz')
|
||||
|
||||
|
||||
def overcloud_ramdisk(basename):
|
||||
return ('%s-initrd' % basename,
|
||||
def overcloud_ramdisk(basename, arch=None):
|
||||
return (_name_helper('%s-initrd' % basename, arch=arch),
|
||||
'.initrd')
|
||||
|
||||
|
||||
def overcloud_image(basename):
|
||||
return (basename,
|
||||
def overcloud_image(basename, arch=None):
|
||||
return (_name_helper(basename, arch=arch),
|
||||
'.qcow2')
|
||||
|
||||
|
||||
def deploy_kernel():
|
||||
return ('bm-deploy-kernel',
|
||||
def deploy_kernel(arch=None):
|
||||
return (_name_helper('bm-deploy-kernel', arch=arch),
|
||||
'.kernel')
|
||||
|
||||
|
||||
def deploy_ramdisk():
|
||||
return ('bm-deploy-ramdisk',
|
||||
def deploy_ramdisk(arch=None):
|
||||
return (_name_helper('bm-deploy-ramdisk', arch=arch),
|
||||
'.initramfs')
|
||||
|
|
|
@ -275,6 +275,13 @@ class UploadOvercloudImage(command.Command):
|
|||
help=_("When set, the overcloud-full image to be uploaded "
|
||||
"will be considered as a whole disk one"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--architecture",
|
||||
help=_("Architecture type for these images, "
|
||||
"\'x86_64\', \'i386\' and \'ppc64le\' "
|
||||
"are common options. This option should match at least "
|
||||
"one \'arch\' value in instackenv.json"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -306,11 +313,15 @@ class UploadOvercloudImage(command.Command):
|
|||
overcloud_image_type)
|
||||
|
||||
properties = {}
|
||||
arch = parsed_args.architecture
|
||||
if arch:
|
||||
properties['hw_architecture'] = arch
|
||||
|
||||
# vmlinuz and initrd only need to be uploaded for a partition image
|
||||
if not parsed_args.whole_disk:
|
||||
(oc_vmlinuz_name,
|
||||
oc_vmlinuz_extension) = plugin_utils.overcloud_kernel(image_name)
|
||||
oc_vmlinuz_extension) = plugin_utils.overcloud_kernel(image_name,
|
||||
arch=arch)
|
||||
oc_vmlinuz_file = os.path.join(parsed_args.image_path,
|
||||
image_name +
|
||||
oc_vmlinuz_extension)
|
||||
|
@ -327,7 +338,8 @@ class UploadOvercloudImage(command.Command):
|
|||
))
|
||||
|
||||
(oc_initrd_name,
|
||||
oc_initrd_extension) = plugin_utils.overcloud_ramdisk(image_name)
|
||||
oc_initrd_extension) = plugin_utils.overcloud_ramdisk(image_name,
|
||||
arch=arch)
|
||||
oc_initrd_file = os.path.join(parsed_args.image_path,
|
||||
image_name +
|
||||
oc_initrd_extension)
|
||||
|
@ -344,7 +356,8 @@ class UploadOvercloudImage(command.Command):
|
|||
))
|
||||
|
||||
(oc_name,
|
||||
oc_extension) = plugin_utils.overcloud_image(image_name)
|
||||
oc_extension) = plugin_utils.overcloud_image(image_name,
|
||||
arch=arch)
|
||||
oc_file = os.path.join(parsed_args.image_path,
|
||||
image_name +
|
||||
oc_extension)
|
||||
|
@ -374,7 +387,8 @@ class UploadOvercloudImage(command.Command):
|
|||
|
||||
else:
|
||||
(oc_name,
|
||||
oc_extension) = plugin_utils.overcloud_image(image_name)
|
||||
oc_extension) = plugin_utils.overcloud_image(image_name,
|
||||
arch=arch)
|
||||
oc_file = os.path.join(parsed_args.image_path,
|
||||
image_name +
|
||||
oc_extension)
|
||||
|
@ -393,7 +407,7 @@ class UploadOvercloudImage(command.Command):
|
|||
self.log.debug("uploading bm images to glance")
|
||||
|
||||
(deploy_kernel_name,
|
||||
deploy_kernel_extension) = plugin_utils.deploy_kernel()
|
||||
deploy_kernel_extension) = plugin_utils.deploy_kernel(arch=arch)
|
||||
deploy_kernel_file = os.path.join(parsed_args.image_path,
|
||||
parsed_args.ipa_name +
|
||||
deploy_kernel_extension)
|
||||
|
@ -409,7 +423,7 @@ class UploadOvercloudImage(command.Command):
|
|||
deploy_kernel_file))
|
||||
|
||||
(deploy_ramdisk_name,
|
||||
deploy_ramdisk_extension) = plugin_utils.deploy_ramdisk()
|
||||
deploy_ramdisk_extension) = plugin_utils.deploy_ramdisk(arch=arch)
|
||||
deploy_ramdisk_file = os.path.join(parsed_args.image_path,
|
||||
parsed_args.ipa_name +
|
||||
deploy_ramdisk_extension)
|
||||
|
|
Loading…
Reference in New Issue