diff --git a/tripleoclient/tests/v1/overcloud_image/test_overcloud_image.py b/tripleoclient/tests/v1/overcloud_image/test_overcloud_image.py index 38219bd69..502735ece 100644 --- a/tripleoclient/tests/v1/overcloud_image/test_overcloud_image.py +++ b/tripleoclient/tests/v1/overcloud_image/test_overcloud_image.py @@ -361,6 +361,7 @@ class TestUploadOvercloudImage(TestPluginV1): # 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={'kernel_id': 10, 'ramdisk_id': 10}, @@ -433,7 +434,7 @@ class TestUploadOvercloudImage(TestPluginV1): ) @mock.patch('subprocess.check_call', autospec=True) - def test_overcloud_create_images(self, mock_subprocess_call): + def test_overcloud_create_images_v2(self, mock_subprocess_call): parsed_args = self.check_parser(self.cmd, [], []) os.path.isfile = mock.Mock(return_value=False) @@ -441,6 +442,57 @@ class TestUploadOvercloudImage(TestPluginV1): 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(name='overcloud-full-vmlinuz', + disk_format='aki', + container_format='bare', + visibility='public'), + mock.call(name='overcloud-full-initrd', + disk_format='ari', + container_format='bare', + visibility='public'), + 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_images_v1(self, mock_subprocess_call): + parsed_args = self.check_parser(self.cmd, [], []) + os.path.isfile = mock.Mock(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 @@ -538,7 +590,7 @@ class TestUploadOvercloudImage(TestPluginV1): self.app.client_manager.image.images.create.call_count ) self.assertEqual( - 5, + 6, self.app.client_manager.image.images.update.call_count ) self.assertEqual(mock_subprocess_call.call_count, 2) diff --git a/tripleoclient/v1/overcloud_image.py b/tripleoclient/v1/overcloud_image.py index c3080c45d..761a3f7ff 100644 --- a/tripleoclient/v1/overcloud_image.py +++ b/tripleoclient/v1/overcloud_image.py @@ -603,6 +603,53 @@ class BuildOvercloudImage(command.Command): manager.build() +class GlanceBaseClientAdapter(object): + def __init__(self, client): + self.client = client + + def print_image_info(self, image): + table = PrettyTable(['ID', 'Name', 'Disk Format', 'Size', 'Status']) + table.add_row([image.id, image.name, image.disk_format, image.size, + image.status]) + print(table, file=sys.stdout) + + +class GlanceV1ClientAdapter(GlanceBaseClientAdapter): + def upload_image(self, *args, **kwargs): + image = self.client.images.create(*args, **kwargs) + + print('Image "%s" was uploaded.' % image.name, file=sys.stdout) + self.print_image_info(image) + return image + + def get_image_property(self, image, prop): + return image.properties[prop] + + +class GlanceV2ClientAdapter(GlanceBaseClientAdapter): + def upload_image(self, *args, **kwargs): + is_public = kwargs.pop('is_public') + data = kwargs.pop('data') + properties = kwargs.pop('properties', None) + kwargs['visibility'] = 'public' if is_public else 'private' + kwargs.setdefault('container_format', 'bare') + + image = self.client.images.create(*args, **kwargs) + + self.client.images.upload(image.id, image_data=data) + if properties: + self.client.images.update(image.id, **properties) + # Refresh image info + image = self.client.images.get(image.id) + + print('Image "%s" was uploaded.' % image.name, file=sys.stdout) + self.print_image_info(image) + return image + + def get_image_property(self, image, prop): + return getattr(image, prop) + + class UploadOvercloudImage(command.Command): """Create overcloud glance images from existing image files.""" log = logging.getLogger(__name__ + ".UploadOvercloudImage") @@ -695,17 +742,11 @@ class UploadOvercloudImage(command.Command): else: self._copy_file(src_file, dest_file) - def _print_image_info(self, image): - table = PrettyTable(['ID', 'Name', 'Disk Format', 'Size', 'Status']) - table.add_row([image.id, image.name, image.disk_format, image.size, - image.status]) - print(table, file=sys.stdout) - - def _upload_image(self, *args, **kwargs): - image = self.app.client_manager.image.images.create(*args, **kwargs) - print('Image "%s" was uploaded.' % image.name, file=sys.stdout) - self._print_image_info(image) - return image + def _get_glance_client_adaptor(self): + if self.app.client_manager.image.version >= 2.0: + return GlanceV2ClientAdapter(self.app.client_manager.image) + else: + return GlanceV1ClientAdapter(self.app.client_manager.image) def get_parser(self, prog_name): parser = super(UploadOvercloudImage, self).get_parser(prog_name) @@ -734,6 +775,7 @@ class UploadOvercloudImage(command.Command): def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) + glance_client_adaptor = self._get_glance_client_adaptor() self._env_variable_or_set('AGENT_NAME', 'ironic-python-agent') @@ -758,7 +800,7 @@ class UploadOvercloudImage(command.Command): kernel = (self._image_try_update(oc_vmlinuz_name, oc_vmlinuz_file, parsed_args) or - self._upload_image( + glance_client_adaptor.upload_image( name=oc_vmlinuz_name, is_public=True, disk_format='aki', @@ -771,7 +813,7 @@ class UploadOvercloudImage(command.Command): ramdisk = (self._image_try_update(oc_initrd_name, oc_initrd_file, parsed_args) or - self._upload_image( + glance_client_adaptor.upload_image( name=oc_initrd_name, is_public=True, disk_format='ari', @@ -783,7 +825,7 @@ class UploadOvercloudImage(command.Command): oc_file = '%s.qcow2' % image_name overcloud_image = (self._image_try_update(oc_name, oc_file, parsed_args) or - self._upload_image( + glance_client_adaptor.upload_image( name=oc_name, is_public=True, disk_format='qcow2', @@ -794,9 +836,12 @@ class UploadOvercloudImage(command.Command): 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 (overcloud_image.properties['kernel_id'] != kernel.id or - overcloud_image.properties['ramdisk_id'] != ramdisk.id): + 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.') @@ -806,25 +851,25 @@ class UploadOvercloudImage(command.Command): deploy_kernel_name = 'bm-deploy-kernel' deploy_kernel_file = '%s.kernel' % os.environ['AGENT_NAME'] self._image_try_update(deploy_kernel_name, deploy_kernel_file, - parsed_args) or self._upload_image( - name=deploy_kernel_name, - is_public=True, - disk_format='aki', - data=self._read_image_file_pointer( - parsed_args.image_path, - deploy_kernel_file) - ) + parsed_args) or \ + glance_client_adaptor.upload_image( + name=deploy_kernel_name, + is_public=True, + disk_format='aki', + data=self._read_image_file_pointer( + parsed_args.image_path, + deploy_kernel_file)) deploy_ramdisk_name = 'bm-deploy-ramdisk' deploy_ramdisk_file = '%s.initramfs' % os.environ['AGENT_NAME'] self._image_try_update(deploy_ramdisk_name, deploy_ramdisk_file, - parsed_args) or self._upload_image( - name=deploy_ramdisk_name, - is_public=True, - disk_format='ari', - data=self._read_image_file_pointer(parsed_args.image_path, - deploy_ramdisk_file) - ) + parsed_args) or \ + glance_client_adaptor.upload_image( + name=deploy_ramdisk_name, + is_public=True, + disk_format='ari', + data=self._read_image_file_pointer(parsed_args.image_path, + deploy_ramdisk_file)) self.log.debug("copy agent images to HTTP BOOT dir")