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:
parent
6a15a7728f
commit
92bd4c43d6
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
Loading…
Reference in New Issue