Add checksum to image LU identification
The SSP disk driver cleverly avoids uploading a particular glance image to the SSP more than once by naming it such that it can be recognized subsequently. If the image LU thus named already exists in the SSP, we reuse it (creating a linked clone for use by the instance being deployed). Previously, the naming convention for an image LU was 'image_%(sanitized_image_name)s', where the sanitized_image_name was generated from the first 73 characters of the glance image name (79, which is the default max filename size, minus the length of 'image_'), scrubbed to replace any non-alphanumeric/period/underscore characters with underscores. This will cause problems whenever an image name is reused while the SSP still contains the original image LU: the SSP disk driver will generate the same name, find an image LU of that name in the SSP, and blithely attach the deploying instance to a linked clone of that image, even though it's not the same as the new one in glance. To remedy this problem, with this change set we are now generating the image LU name as 'image_%(sanitized_image_name)_%(image_checksum)s'. The image_checksum is a 32-character hash that comes from ImageMeta. The total length is still 79; so the 'sanitized_image_name' is now limited to 40 characters. Now, even when an image name is reused, the LU name will (with extremely high probability, like reaching into your average star twice and getting two different milligrams of matter) be different because the image's checksum will be different; so we'll upload and use the new image instead of the old one. Change-Id: I465a90e7105d7d8a7ed1e0b30e96ee98a1d7dfdb
This commit is contained in:
parent
35bc4447db
commit
edf7367ed5
|
@ -14,8 +14,9 @@
|
|||
# under the License.
|
||||
|
||||
from nova.compute import task_states
|
||||
from nova import objects
|
||||
from nova.objects import flavor
|
||||
from nova.objects import image_meta
|
||||
from nova.objects import instance
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -46,8 +47,8 @@ TEST_INSTANCE = {
|
|||
|
||||
TEST_INST_SPAWNING = dict(TEST_INSTANCE, task_state=task_states.SPAWNING)
|
||||
|
||||
TEST_INST1 = objects.Instance(**TEST_INSTANCE)
|
||||
TEST_INST2 = objects.Instance(**TEST_INST_SPAWNING)
|
||||
TEST_INST1 = instance.Instance(**TEST_INSTANCE)
|
||||
TEST_INST2 = instance.Instance(**TEST_INST_SPAWNING)
|
||||
|
||||
TEST_MIGRATION = {
|
||||
'id': 1,
|
||||
|
@ -64,11 +65,11 @@ IMAGE1 = {
|
|||
'name': 'image1',
|
||||
'size': 300,
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'raw'
|
||||
|
||||
'disk_format': 'raw',
|
||||
'checksum': 'b518a8ba2b152b5607aceb5703fac072',
|
||||
}
|
||||
TEST_IMAGE1 = objects.ImageMeta.from_dict(IMAGE1)
|
||||
EMPTY_IMAGE = objects.ImageMeta.from_dict({})
|
||||
TEST_IMAGE1 = image_meta.ImageMeta.from_dict(IMAGE1)
|
||||
EMPTY_IMAGE = image_meta.ImageMeta.from_dict({})
|
||||
|
||||
# NOTE(mikal): All of this is because if dnspython is present in your
|
||||
# environment then eventlet monkeypatches socket.getaddrinfo() with an
|
||||
|
|
|
@ -19,8 +19,10 @@ import fixtures
|
|||
import mock
|
||||
|
||||
import copy
|
||||
from nova.objects import image_meta
|
||||
from nova import test
|
||||
import pypowervm.adapter as pvm_adp
|
||||
from pypowervm import const
|
||||
import pypowervm.entities as pvm_ent
|
||||
from pypowervm.tests import test_fixtures as pvm_fx
|
||||
from pypowervm.tests.test_utils import pvmhttp
|
||||
|
@ -311,7 +313,8 @@ class TestSSPDiskAdapter(test.TestCase):
|
|||
self.assertIn(vios_uuid, ssp_stor.vios_uuids)
|
||||
self.assertEqual(ssp_stor._ssp_wrap, ssp1)
|
||||
# 'image' + '_' + s/-/_/g(image.name), per _get_image_name
|
||||
self.assertEqual('image_' + powervm.TEST_IMAGE1.name, lu_name)
|
||||
self.assertEqual('image_' + powervm.TEST_IMAGE1.name + '_' +
|
||||
powervm.TEST_IMAGE1.checksum, lu_name)
|
||||
self.assertEqual(powervm.TEST_IMAGE1.size, f_size)
|
||||
return 'image_lu', None
|
||||
|
||||
|
@ -334,7 +337,8 @@ class TestSSPDiskAdapter(test.TestCase):
|
|||
mock_crt_lnk_cln):
|
||||
ssp_stor = self._get_ssp_stor()
|
||||
# Mock the 'existing' image LU
|
||||
img_lu = pvm_stg.LU.bld(None, 'image_' + powervm.TEST_IMAGE1.name, 123,
|
||||
img_lu = pvm_stg.LU.bld(None, 'image_' + powervm.TEST_IMAGE1.name + '_'
|
||||
+ powervm.TEST_IMAGE1.checksum, 123,
|
||||
typ=pvm_stg.LUType.IMAGE)
|
||||
ssp_stor._ssp_wrap.logical_units.append(img_lu)
|
||||
|
||||
|
@ -353,6 +357,24 @@ class TestSSPDiskAdapter(test.TestCase):
|
|||
None, Instance(), powervm.TEST_IMAGE1, 1)
|
||||
self.assertEqual('new_lu', lu)
|
||||
|
||||
def test_get_image_name(self):
|
||||
"""Generate image name from ImageMeta."""
|
||||
ssp = self._get_ssp_stor()
|
||||
|
||||
def verify_image_name(name, checksum, expected):
|
||||
img_meta = image_meta.ImageMeta(name=name, checksum=checksum)
|
||||
self.assertEqual(expected, ssp._get_image_name(img_meta))
|
||||
self.assertTrue(len(expected) <= const.MaxLen.FILENAME_DEFAULT)
|
||||
|
||||
verify_image_name('foo', 'bar', 'image_foo_bar')
|
||||
# Ensure a really long name gets truncated properly. Note also '-'
|
||||
# chars are sanitized.
|
||||
verify_image_name(
|
||||
'Template_zw82enbix_PowerVM-CI-18y2385y9123785192364',
|
||||
'b518a8ba2b152b5607aceb5703fac072',
|
||||
'image_Template_zw82enbix_PowerVM_CI_18y2385y91'
|
||||
'_b518a8ba2b152b5607aceb5703fac072')
|
||||
|
||||
def test_find_lu(self):
|
||||
# Bad path, lu not found, None returned
|
||||
ssp = self._get_ssp_stor()
|
||||
|
|
|
@ -266,8 +266,9 @@ class DiskAdapter(object):
|
|||
@staticmethod
|
||||
def _get_image_name(image_meta):
|
||||
"""Generate a name for a virtual storage copy of an image."""
|
||||
return pvm_util.sanitize_file_name_for_api(image_meta.name,
|
||||
prefix=DiskType.IMAGE + '_')
|
||||
return pvm_util.sanitize_file_name_for_api(
|
||||
image_meta.name, prefix=DiskType.IMAGE + '_',
|
||||
suffix='_' + image_meta.checksum)
|
||||
|
||||
@staticmethod
|
||||
def _disk_gb_to_bytes(size_gb, floor=None):
|
||||
|
|
Loading…
Reference in New Issue