'Return changed Properties between the before and after properties.
If any property having immutable as True is updated, raises
NotSupported error.
If any properties have changed which are not in
update_allowed_properties, raises UpdateReplace.'

def update_template_diff_properties(self, after_props, before_props):

    update_allowed_set = self.calc_update_allowed(after_props)
    immutable_set = set()
    for (psk, psv) in six.iteritems(after_props.props):
        if psv.immutable():
            immutable_set.add(psk)
    def prop_changed(key):
        try:
            before = before_props.get(key)
        except (TypeError, ValueError) as exc:
            LOG.warning('Ignoring    error    in    old    property    value    %(prop_name)s:    %(msg)s', {'prop_name': key, 'msg': six.text_type(exc)})
            return True
        return (before != after_props.get(key))
    changed_properties_set = set((k for k in after_props if prop_changed(k)))
    update_replace_forbidden = [k for k in changed_properties_set if (k in immutable_set)]
    if update_replace_forbidden:
        msg = (_('Update    to    properties    %(props)s    of    %(name)s    (%(res)s)') % {'props': ',    '.join(sorted(update_replace_forbidden)), 'res': self.type(), 'name': self.name})
        raise exception.NotSupported(feature=msg)
    if (changed_properties_set and self.needs_replace_with_prop_diff(changed_properties_set, after_props, before_props)):
        raise UpdateReplace(self)
    if (not changed_properties_set.issubset(update_allowed_set)):
        raise UpdateReplace(self.name)
    return dict(((k, after_props.get(k)) for k in changed_properties_set))
