class RegisterImage(ImagesFixture):
    @classmethod
    def tearDownClass(cls):
        cls.resources.release()
        super(RegisterImage, cls).tearDownClass()

    def test_register_image_without_passing_properties(self):
        """
        @summary: Register image without passing any properties

        1) Register image without passing any properties
        2) Verify that the response code is 201
        3) Add the image to the resource pool for deletion
        4) Verify that the response contains the properties and values as
        expected
        """

        errors = []
        id_regex = re.compile(ImageProperties.ID_REGEX)

        resp = self.images.client.register_image()
        image_created_at_time_in_sec = calendar.timegm(time.gmtime())
        self.assertEqual(
            resp.status_code, 201,
            Messages.STATUS_CODE_MSG.format(201, resp.status_code))
        reg_image = resp.entity

        self.resources.add(reg_image.id_, self.images.client.delete_image)

        created_at_delta = self.images.behaviors.get_time_delta(
            image_created_at_time_in_sec, reg_image.created_at)
        updated_at_delta = self.images.behaviors.get_time_delta(
            image_created_at_time_in_sec, reg_image.updated_at)

        if reg_image.auto_disk_config is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('auto_disk_config', 'None',
                                             reg_image.auto_disk_config))
        if reg_image.checksum is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('checksum', 'None',
                                             reg_image.checksum))
        if reg_image.container_format is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('container_format', 'None',
                                             reg_image.container_format))
        if created_at_delta > self.images.config.max_created_at_delta:
            errors.append(
                Messages.PROPERTY_MSG.format(
                    'created_at delta',
                    self.images.config.max_created_at_delta, created_at_delta))
        if reg_image.disk_format is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('disk_format', 'None',
                                             reg_image.disk_format))
        if reg_image.file_ != '/v2/images/{0}/file'.format(reg_image.id_):
            errors.append(
                Messages.PROPERTY_MSG.format(
                    'file_', '/v2/images/{0}/file'.format(reg_image.id_),
                    reg_image.file_))
        if id_regex.match(reg_image.id_) is None:
            errors.append(
                Messages.PROPERTY_MSG.format('id_', 'not None', id_regex))
        if reg_image.image_type is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('image_type', 'None',
                                             reg_image.image_type))
        if reg_image.min_disk != 0:
            errors.append(
                Messages.PROPERTY_MSG.format('min_disk', 0,
                                             reg_image.min_disk))
        if reg_image.min_ram != 0:
            errors.append(
                Messages.PROPERTY_MSG.format('min_ram', 0, reg_image.min_ram))
        if reg_image.name is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('name', 'None', reg_image.name))
        if reg_image.os_type is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('os_type', 'None',
                                             reg_image.os_type))
        if reg_image.owner != self.images.auth.tenant_id:
            errors.append(
                Messages.PROPERTY_MSG.format('owner',
                                             self.images.auth.tenant_id,
                                             reg_image.owner))
        if reg_image.protected is not False:
            errors.append(
                Messages.PROPERTY_MSG.format('protected', False,
                                             reg_image.protected))
        if reg_image.schema != Schemas.IMAGE_SCHEMA:
            errors.append(
                Messages.PROPERTY_MSG.format('schema', Schemas.IMAGE_SCHEMA,
                                             reg_image.schema))
        if reg_image.self_ != '/v2/images/{0}'.format(reg_image.id_):
            errors.append(
                Messages.PROPERTY_MSG.format(
                    'self', '/v2/images/{0}'.format(reg_image.id_),
                    reg_image.self_))
        if reg_image.size is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('size', 'None', reg_image.size))
        if reg_image.status != ImageStatus.QUEUED:
            errors.append(
                Messages.PROPERTY_MSG.format('status', ImageStatus.QUEUED,
                                             reg_image.status))
        if reg_image.tags != []:
            errors.append(
                Messages.PROPERTY_MSG.format('tags', [], reg_image.tags))
        if updated_at_delta > self.images.config.max_updated_at_delta:
            errors.append(
                Messages.PROPERTY_MSG.format(
                    'updated_at delta',
                    self.images.config.max_updated_at_delta, updated_at_delta))
        if reg_image.user_id is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('user_id', 'None',
                                             reg_image.user_id))
        if reg_image.visibility != ImageVisibility.PRIVATE:
            errors.append(
                Messages.PROPERTY_MSG.format('visibility',
                                             ImageVisibility.PRIVATE,
                                             reg_image.visibility))
        if reg_image.additional_properties != {}:
            errors.append(
                Messages.PROPERTY_MSG.format('additional_properties', {},
                                             reg_image.additional_properties))

        self.assertEqual(errors, [],
                         msg=('Unexpected error received. Expected: No errors '
                              'Received: {0}').format(errors))

    @unittest.skip('Redmine bug #11436')
    def test_register_image_passing_all_allowed_properties(self):
        """
        @summary: Register image passing all allowed properties

        1) Register image passing all allowed properties
        2) Verify that the response code is 201
        3) Add the image to the resource pool for deletion
        4) Verify that the response contains values for all allowed properties
        as expected
        """

        errors = []
        id_regex = re.compile(ImageProperties.ID_REGEX)
        auto_disk_config = 'False'
        container_format = ImageContainerFormat.AKI
        disk_format = ImageDiskFormat.ISO
        id_ = '00000000-0000-0000-0000-000000000000'
        image_type = ImageType.IMPORT
        min_disk = images.config.min_disk
        min_ram = images.config.min_ram
        name = rand_name('register_image')
        os_type = ImageOSType.LINUX
        protected = False
        tags = [rand_name('tag1')]
        user_id = random_string()
        additional_properties = {
            self.images.config.additional_property:
            self.images.config.additional_property_value
        }

        resp = self.images.client.register_image(
            auto_disk_config=auto_disk_config,
            container_format=container_format,
            disk_format=disk_format,
            id_=id_,
            image_type=image_type,
            min_disk=min_disk,
            min_ram=min_ram,
            name=name,
            os_type=os_type,
            protected=protected,
            tags=tags,
            user_id=user_id,
            additional_properties=additional_properties)
        self.assertEqual(
            resp.status_code, 201,
            Messages.STATUS_CODE_MSG.format(201, resp.status_code))
        reg_image = resp.entity

        self.resources.add(reg_image.id_, self.images.client.delete_image)

        if reg_image.auto_disk_config != auto_disk_config:
            errors.append(
                Messages.PROPERTY_MSG.format('auto_disk_config',
                                             auto_disk_config,
                                             reg_image.auto_disk_config))
        if reg_image.container_format != container_format:
            errors.append(
                Messages.PROPERTY_MSG.format('container_format',
                                             container_format,
                                             reg_image.container_format))
        if reg_image.disk_format != disk_format:
            errors.append(
                Messages.PROPERTY_MSG.format('disk_format', disk_format,
                                             reg_image.disk_format))
        if id_regex.match(reg_image.id_) is None:
            errors.append(
                Messages.PROPERTY_MSG.format('id_', 'not None', id_regex))
        if reg_image.image_type is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('image_type', 'None',
                                             reg_image.image_type))
        if reg_image.min_disk != min_disk:
            errors.append(
                Messages.PROPERTY_MSG.format('min_disk', min_disk,
                                             reg_image.min_disk))
        if reg_image.min_ram != min_ram:
            errors.append(
                Messages.PROPERTY_MSG.format('min_ram', min_ram,
                                             reg_image.min_ram))
        if reg_image.name != name:
            errors.append(
                Messages.PROPERTY_MSG.format('name', name, reg_image.name))
        if reg_image.os_type is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('os_type', 'None',
                                             reg_image.os_type))
        if reg_image.protected != protected:
            errors.append(
                Messages.PROPERTY_MSG.format('protected', protected,
                                             reg_image.protected))
        if reg_image.tags != tags:
            errors.append(
                Messages.PROPERTY_MSG.format('tags', tags, reg_image.tags))
        if reg_image.user_id is not None:
            errors.append(
                Messages.PROPERTY_MSG.format('user_id', 'None',
                                             reg_image.user_id))
        if reg_image.additional_properties != additional_properties:
            errors.append(
                Messages.PROPERTY_MSG.format('additional_properties',
                                             additional_properties,
                                             reg_image.additional_properties))

        self.assertEqual(errors, [],
                         msg=('Unexpected error received. Expected: No errors '
                              'Received: {0}').format(errors))

    @data_driven_test(
        ImagesDatasetListGenerator.UpdateRegisterImageRestricted())
    def ddtest_register_image_passing_restricted_property(self, prop):
        """
        @summary: Register image passing restricted property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Register image passing restricted property
        2) Verify that the response code is 403
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        # This is a temporary workaround for skips in ddtests
        failure_prop_list = [
            'checksum', 'created_at', 'id', 'image_type', 'location',
            'os_type', 'owner', 'schema', 'size', 'status', 'updated_at',
            'user_id', 'visibility'
        ]
        if prop_key in failure_prop_list:
            sys.stderr.write('skipped \'Redmine bug #11436\' ... ')
            return

        if prop_key == 'file' or prop_key == 'self':
            prop = {'{0}_'.format(prop_key): prop_val}

        resp = self.images.client.register_image(
            name=rand_name('register_image'), **prop)
        self.assertEqual(
            resp.status_code, 403,
            Messages.STATUS_CODE_MSG.format(403, resp.status_code))

    def test_register_two_images_passing_same_name(self):
        """
        @summary: Register two images passing the same name for both

        1) Register image passing only name
        2) Verify that the response code is 201
        3) Add the image to the resource pool for deletion
        4) Register another image passing only the same name
        5) Verify that the response code is 201
        6) Add the image to the resource pool for deletion
        7) Verify that the image ids are not the equal
        """

        name = rand_name('register_image')

        resp = self.images.client.register_image(name=name)
        self.assertEqual(
            resp.status_code, 201,
            Messages.STATUS_CODE_MSG.format(201, resp.status_code))
        reg_image = resp.entity

        self.resources.add(reg_image.id_, self.images.client.delete_image)

        resp = self.images.client.register_image(name=name)
        self.assertEqual(
            resp.status_code, 201,
            Messages.STATUS_CODE_MSG.format(201, resp.status_code))
        alt_reg_image = resp.entity

        self.resources.add(alt_reg_image.id_, self.images.client.delete_image)

        self.assertNotEqual(
            reg_image.id_,
            alt_reg_image.id_,
            msg=('Unexpected image ids received. Expected: Image {0} to be '
                 'different from image {1} '
                 'Received: The same image ids'.format(reg_image.id_,
                                                       alt_reg_image.id_)))

    @data_driven_test(ImagesDatasetListGenerator.RegisterImageInvalidValues())
    def ddtest_register_image_passing_invalid_values(self, prop):
        """
        @summary: Register image passing invalid value for image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Register image passing invalid value for image property
        2) Verify that the response code is 400
        """

        # This is a temporary workaround for skips in ddtests
        if 'auto_disk_config' in prop:
            sys.stderr.write('skipped \'Redmine bug #11438\' ... ')
            return

        if 'name' in prop:
            resp = self.images.client.register_image(**prop)
        else:
            resp = self.images.client.register_image(
                name=rand_name('register_image'), **prop)
        self.assertEqual(
            resp.status_code, 400,
            Messages.STATUS_CODE_MSG.format(400, resp.status_code))

    def test_register_public_image(self):
        """
        @summary: Register a public image by setting the visibility property to
        public

        1) Register image setting the visibility property to public
        2) If the allow_public_images_crud property is true, verify
        that the response code is 201, else verify that the response code is
        403
        3) If the allow_public_images_crud property is true, add the image for
        deletion. List all images accounting for pagination and verify that
        the image is present.
        """

        name = rand_name('register_image')
        resp = self.images.client.register_image(
            name=name, visibility=ImageVisibility.PUBLIC)

        if self.images.config.allow_public_images_crud:
            self.assertEqual(
                resp.status_code, 201,
                Messages.STATUS_CODE_MSG.format(201, resp.status_code))
            reg_image = resp.entity

            self.resources.add(reg_image.id_, self.images.client.delete_image)

            list_images = self.images.behaviors.list_all_images()

            image_names = [image.name for image in list_images]
            self.assertIn(
                name,
                image_names,
                msg=('Unexpected image name received. Expected: {0} in {1} '
                     'Received: Was not present').format(name, image_names))

        else:
            self.assertEqual(
                resp.status_code, 403,
                Messages.STATUS_CODE_MSG.format(403, resp.status_code))
Beispiel #2
0
class UpdateImage(ImagesFixture):
    @classmethod
    def setUpClass(cls):
        super(UpdateImage, cls).setUpClass()

        # Count set to number of images required for this module
        created_images = cls.images.behaviors.create_images_via_task(
            image_properties={'name': rand_name('update_image')}, count=3)

        cls.created_image = created_images.pop()
        cls.alt_created_image = created_images.pop()
        cls.quota_image = created_images.pop()

    @classmethod
    def tearDownClass(cls):
        cls.images.client.update_image(cls.created_image.id_,
                                       replace={'protected': False})
        cls.images.behaviors.resources.release()
        super(UpdateImage, cls).tearDownClass()

    @data_driven_test(ImagesDatasetListGenerator.UpdateImageAllowed())
    def ddtest_update_image_replace_allowed_property(self, prop):
        """
        @summary: Update image replacing allowed image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Update image replacing allowed image property's value
        2) Verify that the response code is 200
        3) Verify that the update image response shows that the property's
        value has been updated as expected
        4) Get image details for the image
        5) Verify that the response is ok
        6) Verify that the get image details response shows that the property's
        value has been updated as expected
        7) Verify that the image's updated_at time has increased
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        resp = self.images.client.update_image(self.created_image.id_,
                                               replace={prop_key: prop_val})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))
        updated_image = resp.entity

        self.assertEqual(
            getattr(updated_image, prop_key),
            prop_val,
            msg=('Unexpected updated image value received. Expected: {0} '
                 'Received: {1}').format(prop_val,
                                         getattr(updated_image, prop_key)))

        resp = self.images.client.get_image_details(self.created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        self.assertEqual(
            getattr(get_image, prop_key),
            prop_val,
            msg=('Unexpected get image details value received. Expected: {0} '
                 'Received: {1}').format(prop_val,
                                         getattr(get_image, prop_key)))

        self.assertGreaterEqual(updated_image.updated_at,
                                self.created_image.updated_at,
                                msg=('Unexpected updated_at value received. '
                                     'Expected: Greater than {0} '
                                     'Received: {1}').format(
                                         self.created_image.updated_at,
                                         updated_image.updated_at))

    @data_driven_test(ImagesDatasetListGenerator.UpdateImageInaccessible())
    def ddtest_update_image_replace_inaccessible_property(self, prop):
        """
        @summary: Update image replacing inaccessible image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Update image replacing inaccessible image property's value
        2) Verify that the response code is 403
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        resp = self.images.client.update_image(self.alt_created_image.id_,
                                               replace={prop_key: prop_val})
        self.assertEqual(
            resp.status_code, 403,
            Messages.STATUS_CODE_MSG.format(403, resp.status_code))

    @data_driven_test(
        ImagesDatasetListGenerator.UpdateRegisterImageRestricted())
    def ddtest_update_image_replace_restricted_property(self, prop):
        """
        @summary: Update image replacing restricted image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Update image replacing restricted image property's value
        2) Verify that the response code is 403
        3) Get image details for the image
        4) Verify that the response is ok
        5) Verify that the get image details response shows that the property's
        value has not been updated
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        # This is a temporary workaround for skips in ddtests
        if prop_key == 'id':
            sys.stderr.write('skipped \'Redmine bug #11398\' ... ')
            return

        resp = self.images.client.update_image(self.alt_created_image.id_,
                                               replace={prop_key: prop_val})
        self.assertEqual(
            resp.status_code, 403,
            Messages.STATUS_CODE_MSG.format(403, resp.status_code))

        resp = self.images.client.get_image_details(self.alt_created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        prop_key = self._check_prop_key(prop_key)

        self.assertEqual(
            getattr(get_image, prop_key),
            getattr(self.alt_created_image, prop_key),
            msg=('Unexpected get image details value received. Expected: {0} '
                 'Received: {1}').format(
                     getattr(self.alt_created_image, prop_key),
                     getattr(get_image, prop_key)))

    @data_driven_test(
        ImagesDatasetListGenerator.UpdateRegisterImageRestricted())
    def ddtest_update_image_add_restricted_property(self, prop):
        """
        @summary: Update image adding restricted image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Update image adding restricted image property
        2) Verify that the response code is 403
        3) Get image details for the image
        4) Verify that the response is ok
        5) Verify that the get image details response shows that the property
        has been not been added
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        # This is a temporary workaround for skips in ddtests
        if prop_key == 'id':
            sys.stderr.write('skipped \'Redmine bug #11398\' ... ')
            return

        resp = self.images.client.update_image(self.alt_created_image.id_,
                                               add={prop_key: prop_val})
        self.assertEqual(
            resp.status_code, 403,
            Messages.STATUS_CODE_MSG.format(403, resp.status_code))

        resp = self.images.client.get_image_details(self.alt_created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        prop_key = self._check_prop_key(prop_key)

        self.assertEqual(
            getattr(get_image, prop_key),
            getattr(self.alt_created_image, prop_key),
            msg=('Unexpected get image details value received. Expected: {0} '
                 'Received: {1}').format(
                     getattr(self.alt_created_image, prop_key),
                     getattr(get_image, prop_key)))

    @data_driven_test(
        ImagesDatasetListGenerator.UpdateRegisterImageRestricted())
    def ddtest_update_image_remove_restricted_property(self, prop):
        """
        @summary: Update image removing restricted image property

        @param prop: Key-value pair containing the image property to validate
        @type prop: Dictionary

        1) Update image removing restricted image property
        2) Verify that the response code is 403
        3) Get image details for the image
        4) Verify that the response is ok
        5) Verify that the get image details response shows that the property
        has been not been removed
        """

        # Each prop passed in only has one key-value pair
        prop_key, prop_val = prop.popitem()

        # This is a temporary workaround for skips in ddtests
        failure_prop_list = ['id', 'file', 'schema', 'self']
        if prop_key in failure_prop_list:
            sys.stderr.write('skipped \'Launchpad bug #1438826\' ... ')
            return

        resp = self.images.client.update_image(self.alt_created_image.id_,
                                               remove={prop_key: prop_val})
        self.assertEqual(
            resp.status_code, 403,
            Messages.STATUS_CODE_MSG.format(403, resp.status_code))

        resp = self.images.client.get_image_details(self.alt_created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        prop_key = self._check_prop_key(prop_key)

        self.assertEqual(
            getattr(get_image, prop_key),
            getattr(self.alt_created_image, prop_key),
            msg=('Unexpected get image details value received. Expected: {0} '
                 'Received: {1}').format(
                     getattr(self.alt_created_image, prop_key),
                     getattr(get_image, prop_key)))

    def test_update_image_add_new_property(self):
        """
        @summary: Update image by adding a new image property

        1) Update image adding a new image property
        2) Verify that the response code is 200
        3) Verify that the update image response shows that the new property
        has been added as expected
        4) Get image details for the image
        5) Verify that the response is ok
        6) Verify that the get image details response shows that the new
        property has been added as expected
        """

        new_prop = 'new_property'
        new_prop_value = rand_name('new_property_value')

        resp = self.images.client.update_image(self.created_image.id_,
                                               add={new_prop: new_prop_value})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))
        updated_image = resp.entity

        added_prop_value = (updated_image.additional_properties.get(
            new_prop, None))

        self.assertEqual(
            added_prop_value,
            new_prop_value,
            msg=('Unexpected new image property value received. Expected: {0} '
                 'Received: {1}').format(new_prop_value, added_prop_value))

        resp = self.images.client.get_image_details(self.created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        get_added_prop_value = (get_image.additional_properties.get(
            new_prop, None))

        self.assertEqual(
            get_added_prop_value,
            new_prop_value,
            msg=('Unexpected new image property value received. Expected: {0} '
                 'Received: {1}').format(new_prop_value, get_added_prop_value))

    def test_update_image_remove_property(self):
        """
        @summary: Update image by removing an image property

        1) Update image adding a new image property
        2) Verify that the response code is 200
        3) Update the image removing the new image property
        4) Verify that the response code is 200
        5) Verify that the update image response shows that the new
        property has been removed as expected
        6) Get image details for the image
        7) Verify that the response code is ok
        8) Verify that the get image response shows that the new
        property has been removed as expected
        """

        new_prop = 'alt_new_property'
        new_prop_value = rand_name('alt_new_property_value')

        resp = self.images.client.update_image(self.created_image.id_,
                                               add={new_prop: new_prop_value})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))

        response = self.images.client.update_image(
            self.created_image.id_, remove={new_prop: new_prop_value})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))
        updated_image = response.entity

        self.assertNotIn(
            new_prop,
            updated_image.additional_properties,
            msg=('Unexpected removed image property received. Expected: {0} '
                 'to not be present '
                 'Received: {1}').format(new_prop,
                                         updated_image.additional_properties))

        resp = self.images.client.get_image_details(self.created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        self.assertNotIn(
            new_prop,
            get_image.additional_properties,
            msg=('Unexpected removed image property received. Expected: {0} '
                 'to not be present '
                 'Received: {1}').format(new_prop,
                                         get_image.additional_properties))

    def test_update_image_replace_additional_property(self):
        """
        @summary: Update image by replacing an additional image property

        1) Update image adding a new image property
        2) Verify that the response code is 200
        3) Update the image replacing the new image property's value
        4) Verify that the response code is 200
        5) Verify that the update image response shows that the new property's
        value has been updated as expected
        6) Get image details for the image
        7) Verify that the response code is ok
        8) Verify that the get image response shows that the new
        property's value has been updated as expected
        """

        new_prop = 'alt_two_new_property'
        new_prop_value = rand_name('alt_two_new_property_value')
        updated_prop_value = rand_name('updated_new_property_value')

        resp = self.images.client.update_image(self.created_image.id_,
                                               add={new_prop: new_prop_value})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))

        resp = self.images.client.update_image(
            self.created_image.id_, replace={new_prop: updated_prop_value})
        self.assertEqual(
            resp.status_code, 200,
            Messages.STATUS_CODE_MSG.format(200, resp.status_code))
        updated_image = resp.entity

        replaced_prop_value = (updated_image.additional_properties.get(
            new_prop, None))

        self.assertEqual(
            replaced_prop_value,
            updated_prop_value,
            msg=('Unexpected updated image property value received. '
                 'Expected: {0} '
                 'Received: {1}').format(updated_prop_value,
                                         replaced_prop_value))

        resp = self.images.client.get_image_details(self.created_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        get_replaced_prop_value = (get_image.additional_properties.get(
            new_prop, None))

        self.assertEqual(
            get_replaced_prop_value,
            updated_prop_value,
            msg=('Unexpected updated image property value received.'
                 'Expected: {0} '
                 'Received: {1}').format(new_prop_value,
                                         get_replaced_prop_value))

    def test_update_image_property_quota_limit(self):
        """
        @summary: Validate image properties quota limit

        1) While the number of image properties is not equal to the image
        properties quota, update image adding a new image property
        2) Verify that the response code is 200
        3) When the number of image properties is equal to the image properties
        quota, update image adding another new image property
        4) Verify that the response code is 413
        5) Get image details
        6) Verify that the response is ok
        7) Verify that the number of image properties matches the image
        properties quota
        """

        number_of_image_properties = 0
        additional_props = [
            'auto_disk_config', 'image_type', 'os_type', 'user_id'
        ]
        quota_limit = self.images.config.image_properties_limit

        # Decrease quota limit for every property that image already contains
        for prop in additional_props:
            if hasattr(self.quota_image, prop):
                quota_limit -= 1

        while number_of_image_properties != quota_limit:
            new_prop = rand_name('prop')
            new_prop_value = rand_name('prop_value')

            resp = self.images.client.update_image(
                self.quota_image.id_, add={new_prop: new_prop_value})
            self.assertEqual(
                resp.status_code, 200,
                Messages.STATUS_CODE_MSG.format(200, resp.status_code))

            resp = self.images.client.get_image_details(self.quota_image.id_)
            self.assertTrue(resp.ok,
                            Messages.OK_RESP_MSG.format(resp.status_code))
            get_image = resp.entity

            number_of_image_properties = len(
                getattr(get_image, 'additional_properties'))

        new_prop = rand_name('prop')
        new_prop_value = rand_name('prop_value')

        resp = self.images.client.update_image(self.quota_image.id_,
                                               add={new_prop: new_prop_value})
        self.assertEqual(
            resp.status_code, 413,
            Messages.STATUS_CODE_MSG.format(413, resp.status_code))

        resp = self.images.client.get_image_details(self.quota_image.id_)
        self.assertTrue(resp.ok, Messages.OK_RESP_MSG.format(resp.status_code))
        get_image = resp.entity

        self.assertEqual(len(getattr(get_image, 'additional_properties')),
                         quota_limit,
                         msg='Unexpected number of image properties returned.'
                         'Expected: {0} '
                         'Received: {1}'.format(
                             quota_limit,
                             len(getattr(get_image, 'additional_properties'))))

    def test_update_image_using_blank_image_id(self):
        """
        @summary: Update image using a blank image id

        1) Update image using blank image id
        2) Verify that the response code is 404
        """

        resp = self.images.client.update_image(
            image_id='', add={'new_prop': rand_name('new_prop_value')})
        self.assertEqual(
            resp.status_code, 404,
            Messages.STATUS_CODE_MSG.format(404, resp.status_code))

    def test_update_image_using_invalid_image_id(self):
        """
        @summary: Update image using an invalid image id

        1) Update image using invalid image id
        2) Verify that the response code is 404
        """

        resp = self.images.client.update_image(
            image_id='invalid', add={'new_prop': rand_name('new_prop_value')})
        self.assertEqual(
            resp.status_code, 404,
            Messages.STATUS_CODE_MSG.format(404, resp.status_code))

    def _check_prop_key(self, prop_key):
        """
        @summary: Check if prop_key needs an underscore added

        @param prop_key: Image property to check
        @type prop_key: String

        @return: Prop_key
        @rtype: String
        """

        keys_need_underscore = ['file', 'id', 'self']

        if prop_key in keys_need_underscore:
            prop_key = '{0}_'.format(prop_key)

        if prop_key == 'location':
            prop_key = 'file_'

        return prop_key