Merge "Add support for Docker image format"
This commit is contained in:
commit
8f5aaead07
@ -566,6 +566,7 @@ Default::
|
|||||||
('aki', _('AKI - Amazon Kernel Image')),
|
('aki', _('AKI - Amazon Kernel Image')),
|
||||||
('ami', _('AMI - Amazon Machine Image')),
|
('ami', _('AMI - Amazon Machine Image')),
|
||||||
('ari', _('ARI - Amazon Ramdisk Image')),
|
('ari', _('ARI - Amazon Ramdisk Image')),
|
||||||
|
('docker', _('Docker')),
|
||||||
('iso', _('ISO - Optical Disk Image')),
|
('iso', _('ISO - Optical Disk Image')),
|
||||||
('qcow2', _('QCOW2 - QEMU Emulator')),
|
('qcow2', _('QCOW2 - QEMU Emulator')),
|
||||||
('raw', _('Raw')),
|
('raw', _('Raw')),
|
||||||
|
@ -102,6 +102,10 @@ class IndexView(tables.DataTableView):
|
|||||||
LOG.warning(invalid_msg)
|
LOG.warning(invalid_msg)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
LOG.warning(invalid_msg)
|
LOG.warning(invalid_msg)
|
||||||
|
elif (filter_field == 'disk_format' and
|
||||||
|
filter_string.lower() == 'docker'):
|
||||||
|
filters['disk_format'] = 'raw'
|
||||||
|
filters['container_format'] = 'docker'
|
||||||
else:
|
else:
|
||||||
filters[filter_field] = filter_string
|
filters[filter_field] = filter_string
|
||||||
return filters
|
return filters
|
||||||
|
@ -37,6 +37,49 @@ IMAGE_BACKEND_SETTINGS = getattr(settings, 'OPENSTACK_IMAGE_BACKEND', {})
|
|||||||
IMAGE_FORMAT_CHOICES = IMAGE_BACKEND_SETTINGS.get('image_formats', [])
|
IMAGE_FORMAT_CHOICES = IMAGE_BACKEND_SETTINGS.get('image_formats', [])
|
||||||
|
|
||||||
|
|
||||||
|
def create_image_metadata(data):
|
||||||
|
"""Use the given dict of image form data to generate the metadata used for
|
||||||
|
creating the image in glance.
|
||||||
|
"""
|
||||||
|
# Glance does not really do anything with container_format at the
|
||||||
|
# moment. It requires it is set to the same disk_format for the three
|
||||||
|
# Amazon image types, otherwise it just treats them as 'bare.' As such
|
||||||
|
# we will just set that to be that here instead of bothering the user
|
||||||
|
# with asking them for information we can already determine.
|
||||||
|
disk_format = data['disk_format']
|
||||||
|
if disk_format in ('ami', 'aki', 'ari',):
|
||||||
|
container_format = disk_format
|
||||||
|
elif disk_format == 'docker':
|
||||||
|
# To support docker containers we allow the user to specify
|
||||||
|
# 'docker' as the format. In that case we really want to use
|
||||||
|
# 'raw' as the disk format and 'docker' as the container format.
|
||||||
|
disk_format = 'raw'
|
||||||
|
container_format = 'docker'
|
||||||
|
else:
|
||||||
|
container_format = 'bare'
|
||||||
|
|
||||||
|
# The Create form uses 'is_public' but the Update form uses 'public'. Just
|
||||||
|
# being tolerant here so we don't break anything else.
|
||||||
|
meta = {'is_public': data.get('is_public', data.get('public', False)),
|
||||||
|
'protected': data['protected'],
|
||||||
|
'disk_format': disk_format,
|
||||||
|
'container_format': container_format,
|
||||||
|
'min_disk': (data['minimum_disk'] or 0),
|
||||||
|
'min_ram': (data['minimum_ram'] or 0),
|
||||||
|
'name': data['name'],
|
||||||
|
'properties': {}}
|
||||||
|
|
||||||
|
if data['description']:
|
||||||
|
meta['properties']['description'] = data['description']
|
||||||
|
if data.get('kernel'):
|
||||||
|
meta['properties']['kernel_id'] = data['kernel']
|
||||||
|
if data.get('ramdisk'):
|
||||||
|
meta['properties']['ramdisk_id'] = data['ramdisk']
|
||||||
|
if data.get('architecture'):
|
||||||
|
meta['properties']['architecture'] = data['architecture']
|
||||||
|
return meta
|
||||||
|
|
||||||
|
|
||||||
class CreateImageForm(forms.SelfHandlingForm):
|
class CreateImageForm(forms.SelfHandlingForm):
|
||||||
name = forms.CharField(max_length=255, label=_("Name"))
|
name = forms.CharField(max_length=255, label=_("Name"))
|
||||||
description = forms.CharField(max_length=255, label=_("Description"),
|
description = forms.CharField(max_length=255, label=_("Description"),
|
||||||
@ -200,33 +243,9 @@ class CreateImageForm(forms.SelfHandlingForm):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
# Glance does not really do anything with container_format at the
|
meta = create_image_metadata(data)
|
||||||
# moment. It requires it is set to the same disk_format for the three
|
|
||||||
# Amazon image types, otherwise it just treats them as 'bare.' As such
|
|
||||||
# we will just set that to be that here instead of bothering the user
|
|
||||||
# with asking them for information we can already determine.
|
|
||||||
if data['disk_format'] in ('ami', 'aki', 'ari',):
|
|
||||||
container_format = data['disk_format']
|
|
||||||
else:
|
|
||||||
container_format = 'bare'
|
|
||||||
|
|
||||||
meta = {'is_public': data['is_public'],
|
# Add image source file or URL to metadata
|
||||||
'protected': data['protected'],
|
|
||||||
'disk_format': data['disk_format'],
|
|
||||||
'container_format': container_format,
|
|
||||||
'min_disk': (data['minimum_disk'] or 0),
|
|
||||||
'min_ram': (data['minimum_ram'] or 0),
|
|
||||||
'name': data['name'],
|
|
||||||
'properties': {}}
|
|
||||||
|
|
||||||
if data['description']:
|
|
||||||
meta['properties']['description'] = data['description']
|
|
||||||
if data.get('kernel'):
|
|
||||||
meta['properties']['kernel_id'] = data['kernel']
|
|
||||||
if data.get('ramdisk'):
|
|
||||||
meta['properties']['ramdisk_id'] = data['ramdisk']
|
|
||||||
if data.get('architecture'):
|
|
||||||
meta['properties']['architecture'] = data['architecture']
|
|
||||||
if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and
|
if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and
|
||||||
policy.check((("image", "upload_image"),), request) and
|
policy.check((("image", "upload_image"),), request) and
|
||||||
data.get('image_file', None)):
|
data.get('image_file', None)):
|
||||||
@ -240,7 +259,7 @@ class CreateImageForm(forms.SelfHandlingForm):
|
|||||||
image = api.glance.image_create(request, **meta)
|
image = api.glance.image_create(request, **meta)
|
||||||
messages.success(request,
|
messages.success(request,
|
||||||
_('Your image %s has been queued for creation.') %
|
_('Your image %s has been queued for creation.') %
|
||||||
data['name'])
|
meta['name'])
|
||||||
return image
|
return image
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = _('Unable to create new image')
|
msg = _('Unable to create new image')
|
||||||
@ -248,7 +267,7 @@ class CreateImageForm(forms.SelfHandlingForm):
|
|||||||
if hasattr(e, 'code') and e.code == 400:
|
if hasattr(e, 'code') and e.code == 400:
|
||||||
if "Invalid disk format" in e.details:
|
if "Invalid disk format" in e.details:
|
||||||
msg = _('Unable to create new image: Invalid disk format '
|
msg = _('Unable to create new image: Invalid disk format '
|
||||||
'%s for image.') % data['disk_format']
|
'%s for image.') % meta['disk_format']
|
||||||
elif "Image name too long" in e.details:
|
elif "Image name too long" in e.details:
|
||||||
msg = _('Unable to create new image: Image name too long.')
|
msg = _('Unable to create new image: Image name too long.')
|
||||||
|
|
||||||
@ -313,26 +332,7 @@ class UpdateImageForm(forms.SelfHandlingForm):
|
|||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
image_id = data['image_id']
|
image_id = data['image_id']
|
||||||
error_updating = _('Unable to update image "%s".')
|
error_updating = _('Unable to update image "%s".')
|
||||||
|
meta = create_image_metadata(data)
|
||||||
if data['disk_format'] in ['aki', 'ari', 'ami']:
|
|
||||||
container_format = data['disk_format']
|
|
||||||
else:
|
|
||||||
container_format = 'bare'
|
|
||||||
|
|
||||||
meta = {'is_public': data['public'],
|
|
||||||
'protected': data['protected'],
|
|
||||||
'disk_format': data['disk_format'],
|
|
||||||
'container_format': container_format,
|
|
||||||
'name': data['name'],
|
|
||||||
'min_ram': (data['minimum_ram'] or 0),
|
|
||||||
'min_disk': (data['minimum_disk'] or 0),
|
|
||||||
'properties': {'description': data['description']}}
|
|
||||||
if data.get('kernel'):
|
|
||||||
meta['properties']['kernel_id'] = data['kernel']
|
|
||||||
if data.get('ramdisk'):
|
|
||||||
meta['properties']['ramdisk_id'] = data['ramdisk']
|
|
||||||
if data.get('architecture'):
|
|
||||||
meta['properties']['architecture'] = data['architecture']
|
|
||||||
# Ensure we do not delete properties that have already been
|
# Ensure we do not delete properties that have already been
|
||||||
# set on an image.
|
# set on an image.
|
||||||
meta['purge_props'] = False
|
meta['purge_props'] = False
|
||||||
|
@ -222,9 +222,12 @@ def get_format(image):
|
|||||||
# which will raise an error if you call upper() on it.
|
# which will raise an error if you call upper() on it.
|
||||||
if not format:
|
if not format:
|
||||||
return format
|
return format
|
||||||
|
if format == "raw":
|
||||||
|
if getattr(image, "container_format") == 'docker':
|
||||||
|
return pgettext_lazy("Image format for display in table",
|
||||||
|
u"Docker")
|
||||||
# Most image formats are untranslated acronyms, but raw is a word
|
# Most image formats are untranslated acronyms, but raw is a word
|
||||||
# and should be translated
|
# and should be translated
|
||||||
if format == "raw":
|
|
||||||
return pgettext_lazy("Image format for display in table", u"Raw")
|
return pgettext_lazy("Image format for display in table", u"Raw")
|
||||||
return format.upper()
|
return format.upper()
|
||||||
|
|
||||||
|
@ -81,6 +81,31 @@ class CreateImageFormTests(test.TestCase):
|
|||||||
source_type_dict = dict(form.fields['source_type'].choices)
|
source_type_dict = dict(form.fields['source_type'].choices)
|
||||||
self.assertNotIn('file', source_type_dict)
|
self.assertNotIn('file', source_type_dict)
|
||||||
|
|
||||||
|
def test_create_image_metadata_docker(self):
|
||||||
|
form_data = {
|
||||||
|
'name': u'Docker image',
|
||||||
|
'description': u'Docker image test',
|
||||||
|
'source_type': u'url',
|
||||||
|
'image_url': u'/',
|
||||||
|
'disk_format': u'docker',
|
||||||
|
'architecture': u'x86-64',
|
||||||
|
'minimum_disk': 15,
|
||||||
|
'minimum_ram': 512,
|
||||||
|
'is_public': False,
|
||||||
|
'protected': False,
|
||||||
|
'is_copying': False
|
||||||
|
}
|
||||||
|
meta = forms.create_image_metadata(form_data)
|
||||||
|
self.assertEqual(meta['disk_format'], 'raw')
|
||||||
|
self.assertEqual(meta['container_format'], 'docker')
|
||||||
|
self.assertIn('properties', meta)
|
||||||
|
self.assertNotIn('description', meta)
|
||||||
|
self.assertNotIn('architecture', meta)
|
||||||
|
self.assertEqual(meta['properties']['description'],
|
||||||
|
form_data['description'])
|
||||||
|
self.assertEqual(meta['properties']['architecture'],
|
||||||
|
form_data['architecture'])
|
||||||
|
|
||||||
|
|
||||||
class UpdateImageFormTests(test.TestCase):
|
class UpdateImageFormTests(test.TestCase):
|
||||||
def test_is_format_field_editable(self):
|
def test_is_format_field_editable(self):
|
||||||
|
@ -79,17 +79,22 @@ class UpdateView(forms.ModalFormView):
|
|||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
image = self.get_object()
|
image = self.get_object()
|
||||||
properties = getattr(image, 'properties', {})
|
properties = getattr(image, 'properties', {})
|
||||||
return {'image_id': self.kwargs['image_id'],
|
data = {'image_id': self.kwargs['image_id'],
|
||||||
'name': getattr(image, 'name', None) or image.id,
|
'name': getattr(image, 'name', None) or image.id,
|
||||||
'description': properties.get('description', ''),
|
'description': properties.get('description', ''),
|
||||||
'kernel': properties.get('kernel_id', ''),
|
'kernel': properties.get('kernel_id', ''),
|
||||||
'ramdisk': properties.get('ramdisk_id', ''),
|
'ramdisk': properties.get('ramdisk_id', ''),
|
||||||
'architecture': properties.get('architecture', ''),
|
'architecture': properties.get('architecture', ''),
|
||||||
'disk_format': getattr(image, 'disk_format', None),
|
|
||||||
'minimum_ram': getattr(image, 'min_ram', None),
|
'minimum_ram': getattr(image, 'min_ram', None),
|
||||||
'minimum_disk': getattr(image, 'min_disk', None),
|
'minimum_disk': getattr(image, 'min_disk', None),
|
||||||
'public': getattr(image, 'is_public', None),
|
'public': getattr(image, 'is_public', None),
|
||||||
'protected': getattr(image, 'protected', None)}
|
'protected': getattr(image, 'protected', None)}
|
||||||
|
disk_format = getattr(image, 'disk_format', None)
|
||||||
|
if (disk_format == 'raw' and
|
||||||
|
getattr(image, 'container_format') == 'docker'):
|
||||||
|
disk_format = 'docker'
|
||||||
|
data['disk_format'] = disk_format
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class DetailView(tabs.TabView):
|
class DetailView(tabs.TabView):
|
||||||
|
@ -265,6 +265,7 @@ OPENSTACK_NEUTRON_NETWORK = {
|
|||||||
# ('aki', _('AKI - Amazon Kernel Image')),
|
# ('aki', _('AKI - Amazon Kernel Image')),
|
||||||
# ('ami', _('AMI - Amazon Machine Image')),
|
# ('ami', _('AMI - Amazon Machine Image')),
|
||||||
# ('ari', _('ARI - Amazon Ramdisk Image')),
|
# ('ari', _('ARI - Amazon Ramdisk Image')),
|
||||||
|
# ('docker', _('Docker')),
|
||||||
# ('iso', _('ISO - Optical Disk Image')),
|
# ('iso', _('ISO - Optical Disk Image')),
|
||||||
# ('ova', _('OVA - Open Virtual Appliance')),
|
# ('ova', _('OVA - Open Virtual Appliance')),
|
||||||
# ('qcow2', _('QCOW2 - QEMU Emulator')),
|
# ('qcow2', _('QCOW2 - QEMU Emulator')),
|
||||||
|
@ -81,6 +81,7 @@ OPENSTACK_IMAGE_BACKEND = {
|
|||||||
('aki', _('AKI - Amazon Kernel Image')),
|
('aki', _('AKI - Amazon Kernel Image')),
|
||||||
('ami', _('AMI - Amazon Machine Image')),
|
('ami', _('AMI - Amazon Machine Image')),
|
||||||
('ari', _('ARI - Amazon Ramdisk Image')),
|
('ari', _('ARI - Amazon Ramdisk Image')),
|
||||||
|
('docker', _('Docker')),
|
||||||
('iso', _('ISO - Optical Disk Image')),
|
('iso', _('ISO - Optical Disk Image')),
|
||||||
('ova', _('OVA - Open Virtual Appliance')),
|
('ova', _('OVA - Open Virtual Appliance')),
|
||||||
('qcow2', _('QCOW2 - QEMU Emulator')),
|
('qcow2', _('QCOW2 - QEMU Emulator')),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user