Allow to specify the image type to upload

When trying to just replace a single image in the overcloud,
the client is asking for all the images to be present in
the working directory. Allowing to pass an image-type flag
(with os/ipa values), that will restrict the upload to that type.

Change-Id: I4e48458f83d2a646aad873b0577dc3bea766d2d7
Fixes-Bug: #1778500
This commit is contained in:
Yolanda Robla 2018-06-25 14:06:35 +02:00
parent fa372dee34
commit 84a270a418
3 changed files with 276 additions and 137 deletions

View File

@ -0,0 +1,7 @@
---
features:
- Add a new feature called image-type, that accepts 'os' and
'ironic-python-agent' values.
When specified, it restricts the image to upload to that type, making it
easier to replace ipa/os images without having to collect the full set in
our working directory.

View File

@ -831,3 +831,120 @@ class TestUploadOvercloudImageFullMultiArch(TestPluginV1):
mock.call('sudo cp -f "./ironic-python-agent.initramfs" ' mock.call('sudo cp -f "./ironic-python-agent.initramfs" '
'"/httpboot/p9-ppc64le/agent.ramdisk"', shell=True), '"/httpboot/p9-ppc64le/agent.ramdisk"', shell=True),
]) ])
class TestUploadOnlyExisting(TestPluginV1):
def setUp(self):
super(TestUploadOnlyExisting, 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
self.app.client_manager.image.images.create.return_value = (
mock.Mock(id=10, name='imgname', properties={},
created_at='2015-07-31T14:37:22.000000'))
self.cmd._check_file_exists = mock.Mock()
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('os.path.isfile', autospec=True)
def test_overcloud_upload_just_ipa_wholedisk(
self, mock_isfile_call, mock_subprocess_call):
self.cmd._image_changed = mock.Mock(return_value=True)
self.cmd._image_try_update = mock.Mock(return_value=None)
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
parsed_args = self.check_parser(
self.cmd, ['--whole-disk', '--image-type=ironic-python-agent'], [])
self.cmd._files_changed = mock.Mock(return_value=True)
self.cmd.take_action(parsed_args)
# ensure check_file_exists has not been called
self.assertItemsEqual(self.cmd._check_file_exists.call_args_list,
[mock.call('./ironic-python-agent.initramfs'),
mock.call('./ironic-python-agent.kernel')])
# ensure try_update has been called just with ipa
files = []
for item in self.cmd._image_try_update.call_args_list:
files.append(item[0][1])
self.assertEqual(files, ['./ironic-python-agent.kernel',
'./ironic-python-agent.initramfs'])
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('os.path.isfile', autospec=True)
def test_overcloud_upload_just_os_wholedisk(
self, mock_isfile_call, mock_subprocess_call):
self.cmd._image_changed = mock.Mock(return_value=True)
self.cmd._image_try_update = mock.Mock(return_value=None)
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
parsed_args = self.check_parser(
self.cmd, ['--whole-disk', '--image-type=os'], [])
self.cmd._files_changed = mock.Mock(return_value=True)
self.cmd.take_action(parsed_args)
# ensure check_file_exists has been called just with ipa
self.assertItemsEqual(self.cmd._check_file_exists.call_args_list,
[mock.call('./overcloud-full.qcow2')])
# ensure try_update has been called just with ipa
files = []
for item in self.cmd._image_try_update.call_args_list:
files.append(item[0][1])
self.assertEqual(files, ['./overcloud-full.qcow2'])
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('os.path.isfile', autospec=True)
def test_overcloud_upload_just_ipa(
self, mock_isfile_call, mock_subprocess_call):
self.cmd._image_changed = mock.Mock(return_value=True)
self.cmd._image_try_update = mock.Mock(return_value=None)
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
parsed_args = self.check_parser(
self.cmd, ['--image-type=ironic-python-agent'], [])
self.cmd._files_changed = mock.Mock(return_value=True)
self.cmd.take_action(parsed_args)
# ensure check_file_exists has been called just with ipa
self.assertItemsEqual(self.cmd._check_file_exists.call_args_list,
[mock.call('./ironic-python-agent.initramfs'),
mock.call('./ironic-python-agent.kernel')])
# ensure try_update has been called just with ipa
files = []
for item in self.cmd._image_try_update.call_args_list:
files.append(item[0][1])
self.assertEqual(files, ['./ironic-python-agent.kernel',
'./ironic-python-agent.initramfs'])
@mock.patch('subprocess.check_call', autospec=True)
@mock.patch('os.path.isfile', autospec=True)
def test_overcloud_upload_just_os(
self, mock_isfile_call, mock_subprocess_call):
self.cmd._image_changed = mock.Mock(return_value=True)
self.cmd._image_try_update = mock.Mock(return_value=None)
self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
parsed_args = self.check_parser(
self.cmd, ['--image-type=os'], [])
self.cmd._files_changed = mock.Mock(return_value=True)
self.cmd.take_action(parsed_args)
# ensure check_file_exists has been called just with ipa
self.assertItemsEqual(self.cmd._check_file_exists.call_args_list,
[mock.call('./overcloud-full.qcow2')])
# ensure try_update has been called just with ipa
files = []
for item in self.cmd._image_try_update.call_args_list:
files.append(item[0][1])
self.assertEqual(files, ['./overcloud-full.vmlinuz',
'./overcloud-full.initrd',
'./overcloud-full.qcow2'])

View File

@ -289,6 +289,14 @@ class UploadOvercloudImage(command.Command):
"generic images for x86_64 but offer images specific to " "generic images for x86_64 but offer images specific to "
"SandyBridge (SNB)."), "SandyBridge (SNB)."),
) )
parser.add_argument(
"--image-type",
dest="image_type",
choices=["os", "ironic-python-agent"],
help=_("If specified, allows to restrict the image type to upload "
"(os for the overcloud image or ironic-python-agent for "
"the ironic-python-agent one)"),
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -302,17 +310,18 @@ class UploadOvercloudImage(command.Command):
self.log.debug("checking if image files exist") self.log.debug("checking if image files exist")
image_files = []
if parsed_args.image_type is None or \
parsed_args.image_type == 'ironic-python-agent':
image_files.append('%s.initramfs' % parsed_args.ipa_name)
image_files.append('%s.kernel' % parsed_args.ipa_name)
if parsed_args.image_type is None or parsed_args.image_type == 'os':
image_files.append(parsed_args.os_image_name)
if parsed_args.whole_disk: if parsed_args.whole_disk:
image_files = [
parsed_args.os_image_name
]
overcloud_image_type = 'whole disk' overcloud_image_type = 'whole disk'
else: else:
image_files = [
'%s.initramfs' % parsed_args.ipa_name,
'%s.kernel' % parsed_args.ipa_name,
parsed_args.os_image_name
]
overcloud_image_type = 'partition' overcloud_image_type = 'partition'
for image in image_files: for image in image_files:
@ -332,143 +341,149 @@ class UploadOvercloudImage(command.Command):
if platform: if platform:
properties['tripleo_platform'] = platform properties['tripleo_platform'] = platform
# vmlinuz and initrd only need to be uploaded for a partition image if parsed_args.image_type is None or parsed_args.image_type == 'os':
if not parsed_args.whole_disk: # vmlinuz and initrd only need to be uploaded for a partition image
(oc_vmlinuz_name, if not parsed_args.whole_disk:
oc_vmlinuz_extension) = plugin_utils.overcloud_kernel( (oc_vmlinuz_name,
image_name, arch=arch, platform=platform) oc_vmlinuz_extension) = plugin_utils.overcloud_kernel(
oc_vmlinuz_file = os.path.join(parsed_args.image_path, image_name, arch=arch, platform=platform)
image_name + oc_vmlinuz_file = os.path.join(parsed_args.image_path,
oc_vmlinuz_extension) image_name +
kernel = (self._image_try_update(oc_vmlinuz_name, oc_vmlinuz_extension)
oc_vmlinuz_file, kernel = (self._image_try_update(oc_vmlinuz_name,
parsed_args) or oc_vmlinuz_file,
glance_client_adaptor.upload_image( parsed_args) or
name=oc_vmlinuz_name, glance_client_adaptor.upload_image(
is_public=True, name=oc_vmlinuz_name,
disk_format='aki', is_public=True,
properties=properties, disk_format='aki',
data=self._read_image_file_pointer( properties=properties,
parsed_args.image_path, oc_vmlinuz_file) data=self._read_image_file_pointer(
)) parsed_args.image_path, oc_vmlinuz_file)
))
(oc_initrd_name, (oc_initrd_name,
oc_initrd_extension) = plugin_utils.overcloud_ramdisk( oc_initrd_extension) = plugin_utils.overcloud_ramdisk(
image_name, arch=arch, platform=platform) image_name, arch=arch, platform=platform)
oc_initrd_file = os.path.join(parsed_args.image_path, oc_initrd_file = os.path.join(parsed_args.image_path,
image_name + image_name +
oc_initrd_extension) oc_initrd_extension)
ramdisk = (self._image_try_update(oc_initrd_name, ramdisk = (self._image_try_update(oc_initrd_name,
oc_initrd_file, oc_initrd_file,
parsed_args) or parsed_args) or
glance_client_adaptor.upload_image( glance_client_adaptor.upload_image(
name=oc_initrd_name, name=oc_initrd_name,
is_public=True, is_public=True,
disk_format='ari', disk_format='ari',
properties=properties, properties=properties,
data=self._read_image_file_pointer( data=self._read_image_file_pointer(
parsed_args.image_path, oc_initrd_file) parsed_args.image_path, oc_initrd_file)
)) ))
(oc_name, (oc_name,
oc_extension) = plugin_utils.overcloud_image( oc_extension) = plugin_utils.overcloud_image(
image_name, arch=arch, platform=platform) image_name, arch=arch, platform=platform)
oc_file = os.path.join(parsed_args.image_path, oc_file = os.path.join(parsed_args.image_path,
image_name + image_name +
oc_extension) oc_extension)
overcloud_image = (self._image_try_update(oc_name, oc_file, overcloud_image = (self._image_try_update(oc_name, oc_file,
parsed_args) or parsed_args) or
glance_client_adaptor.upload_image( glance_client_adaptor.upload_image(
name=oc_name, name=oc_name,
is_public=True, is_public=True,
disk_format='qcow2', disk_format='qcow2',
container_format='bare', container_format='bare',
properties=dict({'kernel_id': kernel.id, properties=dict(
'ramdisk_id': ramdisk.id}, {'kernel_id': kernel.id,
**properties), 'ramdisk_id': ramdisk.id},
data=self._read_image_file_pointer( **properties),
parsed_args.image_path, oc_file) data=self._read_image_file_pointer(
)) parsed_args.image_path, oc_file)
))
img_kernel_id = glance_client_adaptor.get_image_property( img_kernel_id = glance_client_adaptor.get_image_property(
overcloud_image, 'kernel_id') overcloud_image, 'kernel_id')
img_ramdisk_id = glance_client_adaptor.get_image_property( img_ramdisk_id = glance_client_adaptor.get_image_property(
overcloud_image, 'ramdisk_id') overcloud_image, 'ramdisk_id')
# check overcloud image links # check overcloud image links
if (img_kernel_id != kernel.id or img_ramdisk_id != ramdisk.id): if (img_kernel_id != kernel.id or
self.log.error('Link overcloud image to it\'s initrd and ' img_ramdisk_id != ramdisk.id):
'kernel images is MISSING OR leads to OLD ' self.log.error('Link overcloud image to it\'s initrd and '
'image. You can keep it or fix it manually.') 'kernel images is MISSING OR leads to OLD '
'image. You can keep it or fix it '
'manually.')
else: else:
(oc_name, (oc_name,
oc_extension) = plugin_utils.overcloud_image( oc_extension) = plugin_utils.overcloud_image(
image_name, arch=arch, platform=platform) image_name, arch=arch, platform=platform)
oc_file = os.path.join(parsed_args.image_path, oc_file = os.path.join(parsed_args.image_path,
image_name + image_name +
oc_extension) oc_extension)
overcloud_image = (self._image_try_update(oc_name, oc_file, overcloud_image = (self._image_try_update(oc_name, oc_file,
parsed_args) or parsed_args) or
glance_client_adaptor.upload_image( glance_client_adaptor.upload_image(
name=oc_name, name=oc_name,
is_public=True, is_public=True,
disk_format='qcow2', disk_format='qcow2',
container_format='bare', container_format='bare',
properties=properties, properties=properties,
data=self._read_image_file_pointer( data=self._read_image_file_pointer(
parsed_args.image_path, oc_file) parsed_args.image_path, oc_file)
)) ))
self.log.debug("uploading bm images to glance") self.log.debug("uploading bm images to glance")
(deploy_kernel_name, if parsed_args.image_type is None or \
deploy_kernel_extension) = plugin_utils.deploy_kernel( parsed_args.image_type == 'ironic-python-agent':
arch=arch, platform=platform) (deploy_kernel_name,
deploy_kernel_file = os.path.join(parsed_args.image_path, deploy_kernel_extension) = plugin_utils.deploy_kernel(
parsed_args.ipa_name + arch=arch, platform=platform)
deploy_kernel_extension) deploy_kernel_file = os.path.join(parsed_args.image_path,
self._image_try_update(deploy_kernel_name, deploy_kernel_file, parsed_args.ipa_name +
parsed_args) or \ deploy_kernel_extension)
glance_client_adaptor.upload_image( self._image_try_update(deploy_kernel_name, deploy_kernel_file,
name=deploy_kernel_name, parsed_args) or \
is_public=True, glance_client_adaptor.upload_image(
disk_format='aki', name=deploy_kernel_name,
properties=properties, is_public=True,
data=self._read_image_file_pointer( disk_format='aki',
parsed_args.image_path, properties=properties,
deploy_kernel_file)) data=self._read_image_file_pointer(
parsed_args.image_path,
deploy_kernel_file))
(deploy_ramdisk_name, (deploy_ramdisk_name,
deploy_ramdisk_extension) = plugin_utils.deploy_ramdisk( deploy_ramdisk_extension) = plugin_utils.deploy_ramdisk(
arch=arch, platform=platform) arch=arch, platform=platform)
deploy_ramdisk_file = os.path.join(parsed_args.image_path, deploy_ramdisk_file = os.path.join(parsed_args.image_path,
parsed_args.ipa_name + parsed_args.ipa_name +
deploy_ramdisk_extension) deploy_ramdisk_extension)
self._image_try_update(deploy_ramdisk_name, deploy_ramdisk_file, self._image_try_update(deploy_ramdisk_name, deploy_ramdisk_file,
parsed_args) or \ parsed_args) or \
glance_client_adaptor.upload_image( glance_client_adaptor.upload_image(
name=deploy_ramdisk_name, name=deploy_ramdisk_name,
is_public=True, is_public=True,
disk_format='ari', disk_format='ari',
properties=properties, properties=properties,
data=self._read_image_file_pointer(parsed_args.image_path, data=self._read_image_file_pointer(parsed_args.image_path,
deploy_ramdisk_file)) deploy_ramdisk_file))
self.log.debug("copy agent images to HTTP BOOT dir") self.log.debug("copy agent images to HTTP BOOT dir")
# TODO(tonyb) Decide how to handle platform specific httpboot # TODO(tonyb) Decide how to handle platform specific httpboot
# files/names # files/names
self._file_create_or_update( self._file_create_or_update(
os.path.join(parsed_args.image_path, os.path.join(parsed_args.image_path,
'%s.kernel' % parsed_args.ipa_name), '%s.kernel' % parsed_args.ipa_name),
os.path.join(parsed_args.http_boot, 'agent.kernel'), os.path.join(parsed_args.http_boot, 'agent.kernel'),
parsed_args.update_existing parsed_args.update_existing
) )
self._file_create_or_update( self._file_create_or_update(
os.path.join(parsed_args.image_path, os.path.join(parsed_args.image_path,
'%s.initramfs' % parsed_args.ipa_name), '%s.initramfs' % parsed_args.ipa_name),
os.path.join(parsed_args.http_boot, 'agent.ramdisk'), os.path.join(parsed_args.http_boot, 'agent.ramdisk'),
parsed_args.update_existing parsed_args.update_existing
) )