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:
parent
b562ff4c36
commit
f41895e374
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
Loading…
Reference in New Issue