Enable creating images with Glance v2

The main difference between the interface for Glance v1 and Glance v2
is the definition of the image's visibility, which in v1 is defined
by a boolean called 'is_public', while in v2 it's a string valled
'visibility'. This enables the usage of both, depending on the
version used by the deployer.

Change-Id: Ife16a6997c60ddc941527ab51cb3fe2ab0b24c5a
This commit is contained in:
Juan Antonio Osorio Robles 2016-10-17 16:26:51 +03:00
parent b562ff4c36
commit f41895e374
2 changed files with 130 additions and 33 deletions

View File

@ -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)

View File

@ -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")