Support whole disk images in TripleO

Enable to upload whole disk images from the client,
not forcing to upload kernel and initrd images, and
not setting the parameters for those in the glance
qcow2 image.

Change-Id: I9fbac888b3e709212cc6c72153a3ca465101987c
Partially-Implements: blueprint support-full-disk-images
This commit is contained in:
Yolanda Robla Mota 2016-11-07 14:23:26 +01:00
parent 6a15a7728f
commit 92bd4c43d6
3 changed files with 217 additions and 53 deletions

View File

@ -0,0 +1,15 @@
---
features:
- Allow client to support whole disk images. Client
will now accept a --whole-disk flag on the
overcloud image upload command. When this flag is
set, it will only look for qcow2 image, not enforcing
the upload of initrd and vmlinuz images. It will also
not set these properties on the qcow2 image on glance.
This will allow Ironic to consider the uploaded image
as full disk image, giving the possibility to provide
full disk images in TripleO instead of single partition
ones.
Please look at `Ironic documentation <http://docs.openstack.org/project-install-guide/baremetal/draft/configure-integration.html#create-and-add-images-to-the-image-service>`_
for reference

View File

@ -594,3 +594,119 @@ class TestUploadOvercloudImage(TestPluginV1):
self.app.client_manager.image.images.update.call_count
)
self.assertEqual(mock_subprocess_call.call_count, 2)
class TestUploadOvercloudImageFull(TestPluginV1):
def setUp(self):
super(TestUploadOvercloudImageFull, 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._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA')
self.cmd._check_file_exists = mock.Mock(return_value=True)
@mock.patch('subprocess.check_call', autospec=True)
def test_overcloud_create_images(self, mock_subprocess_call):
parsed_args = self.check_parser(self.cmd, ['--whole-disk'], [])
os.path.isfile = mock.Mock(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='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')
], self.app.client_manager.image.images.create.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('subprocess.check_call', autospec=True)
def test_overcloud_create_noupdate_images(self, mock_subprocess_call):
parsed_args = self.check_parser(self.cmd, ['--whole-disk'], [])
os.path.isfile = mock.Mock(return_value=True)
self.cmd._files_changed = mock.Mock(return_value=True)
existing_image = mock.Mock(id=10, name='imgname',
properties={})
self.cmd._get_image = mock.Mock(return_value=existing_image)
self.cmd._image_changed = mock.Mock(return_value=True)
self.cmd.take_action(parsed_args)
self.assertEqual(
0,
self.app.client_manager.image.images.delete.call_count
)
self.assertEqual(
0,
self.app.client_manager.image.images.create.call_count
)
self.assertEqual(
0,
self.app.client_manager.image.images.update.call_count
)
self.assertEqual(mock_subprocess_call.call_count, 0)
@mock.patch('subprocess.check_call', autospec=True)
def test_overcloud_create_update_images(self, mock_subprocess_call):
parsed_args = self.check_parser(
self.cmd, ['--update-existing', '--whole-disk'], [])
self.cmd._files_changed = mock.Mock(return_value=True)
existing_image = mock.Mock(id=10, name='imgname',
properties={},
created_at='2015-07-31T14:37:22.000000')
self.cmd._get_image = mock.Mock(return_value=existing_image)
self.cmd._image_changed = mock.Mock(return_value=True)
self.app.client_manager.image.images.update.return_value = (
existing_image)
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(
3,
self.app.client_manager.image.images.update.call_count
)
self.assertEqual(mock_subprocess_call.call_count, 2)

View File

@ -771,6 +771,14 @@ class UploadOvercloudImage(command.Command):
action="store_true",
help=_("Update images if already exist"),
)
parser.add_argument(
"--whole-disk",
dest="whole_disk",
action="store_true",
default=False,
help=_("When set, the overcloud-full image to be uploaded "
"will be considered as a whole disk one"),
)
return parser
def take_action(self, parsed_args):
@ -781,11 +789,18 @@ class UploadOvercloudImage(command.Command):
self.log.debug("checking if image files exist")
image_files = [
'%s.initramfs' % os.environ['AGENT_NAME'],
'%s.kernel' % os.environ['AGENT_NAME'],
parsed_args.os_image
]
if parsed_args.whole_disk:
image_files = [
parsed_args.os_image
]
overcloud_image_type = 'whole disk'
else:
image_files = [
'%s.initramfs' % os.environ['AGENT_NAME'],
'%s.kernel' % os.environ['AGENT_NAME'],
parsed_args.os_image
]
overcloud_image_type = 'partition'
for image in image_files:
self._check_file_exists(os.path.join(parsed_args.image_path,
@ -793,58 +808,76 @@ class UploadOvercloudImage(command.Command):
image_name = parsed_args.os_image.split('.')[0]
self.log.debug("uploading overcloud images to glance")
self.log.debug("uploading %s overcloud images to glance" %
overcloud_image_type)
oc_vmlinuz_name = '%s-vmlinuz' % image_name
oc_vmlinuz_file = '%s.vmlinuz' % image_name
kernel = (self._image_try_update(oc_vmlinuz_name,
oc_vmlinuz_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_vmlinuz_name,
is_public=True,
disk_format='aki',
data=self._read_image_file_pointer(
parsed_args.image_path, oc_vmlinuz_file)
))
# vmlinuz and initrd only need to be uploaded for a partition image
if not parsed_args.whole_disk:
oc_vmlinuz_name = '%s-vmlinuz' % image_name
oc_vmlinuz_file = '%s.vmlinuz' % image_name
kernel = (self._image_try_update(oc_vmlinuz_name,
oc_vmlinuz_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_vmlinuz_name,
is_public=True,
disk_format='aki',
data=self._read_image_file_pointer(
parsed_args.image_path, oc_vmlinuz_file)
))
oc_initrd_name = '%s-initrd' % image_name
oc_initrd_file = '%s.initrd' % image_name
ramdisk = (self._image_try_update(oc_initrd_name,
oc_initrd_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_initrd_name,
is_public=True,
disk_format='ari',
data=self._read_image_file_pointer(
parsed_args.image_path, oc_initrd_file)
))
oc_initrd_name = '%s-initrd' % image_name
oc_initrd_file = '%s.initrd' % image_name
ramdisk = (self._image_try_update(oc_initrd_name,
oc_initrd_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_initrd_name,
is_public=True,
disk_format='ari',
data=self._read_image_file_pointer(
parsed_args.image_path, oc_initrd_file)
))
oc_name = image_name
oc_file = '%s.qcow2' % image_name
overcloud_image = (self._image_try_update(oc_name, oc_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_name,
is_public=True,
disk_format='qcow2',
container_format='bare',
properties={'kernel_id': kernel.id,
'ramdisk_id': ramdisk.id},
data=self._read_image_file_pointer(
parsed_args.image_path, oc_file)
))
oc_name = image_name
oc_file = '%s.qcow2' % image_name
overcloud_image = (self._image_try_update(oc_name, oc_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_name,
is_public=True,
disk_format='qcow2',
container_format='bare',
properties={'kernel_id': kernel.id,
'ramdisk_id': ramdisk.id},
data=self._read_image_file_pointer(
parsed_args.image_path, oc_file)
))
img_kernel_id = glance_client_adaptor.get_image_property(
overcloud_image, 'kernel_id')
img_ramdisk_id = glance_client_adaptor.get_image_property(
overcloud_image, 'ramdisk_id')
# check overcloud image links
if (img_kernel_id != kernel.id or img_ramdisk_id != ramdisk.id):
self.log.error('Link overcloud image to it\'s initrd and kernel'
' images is MISSING OR leads to OLD image.'
' You can keep it or fix it manually.')
img_kernel_id = glance_client_adaptor.get_image_property(
overcloud_image, 'kernel_id')
img_ramdisk_id = glance_client_adaptor.get_image_property(
overcloud_image, 'ramdisk_id')
# check overcloud image links
if (img_kernel_id != kernel.id or img_ramdisk_id != ramdisk.id):
self.log.error('Link overcloud image to it\'s initrd and '
'kernel images is MISSING OR leads to OLD '
'image. You can keep it or fix it manually.')
else:
oc_name = image_name
oc_file = '%s.qcow2' % image_name
overcloud_image = (self._image_try_update(oc_name, oc_file,
parsed_args) or
glance_client_adaptor.upload_image(
name=oc_name,
is_public=True,
disk_format='qcow2',
container_format='bare',
properties={},
data=self._read_image_file_pointer(
parsed_args.image_path, oc_file)
))
self.log.debug("uploading bm images to glance")