첫 컨트리뷰션이 될 이슈를 이것 으로 결정했다.
오픈스택 대시보드의 설정 화면에서 비밀번호를 변경하는 경우, 500 Internal Server Error
가 발생한다. 문제를 찾기 위해 오픈스택 대시보드를 관리하는 프로젝트인 horizon의 로그 (/var/log/apache2/horizon_error.log) 를 확인했다.
2020-09-05 07:55:33.775492 DEBUG keystoneauth.session GET call to identity for http://192.168.1.10/identity/v3/users/a6afb486ad9b4200a4ed37b6865f4e65 used request id req-7d8bed77-9849-4d5e-8a6c-5963bffbfea4
2020-09-05 07:55:33.775714 DEBUG openstack_dashboard.api.keystone Creating a new keystoneclient connection to http://192.168.1.10/identity/v3.
2020-09-05 07:55:33.835818 mod_wsgi (pid=18843): Exception occurred processing WSGI script '/opt/stack/horizon/openstack_dashboard/wsgi.py'.
2020-09-05 07:55:33.835884 Traceback (most recent call last):
2020-09-05 07:55:33.835899 File "/usr/local/lib/python3.6/dist-packages/django/core/handlers/wsgi.py", line 150, in __call__
2020-09-05 07:55:33.835901 start_response(status, response_headers)
2020-09-05 07:55:33.835910 ValueError: unicode object contains non latin-1 characters
로그를 확인하니 값을 넘겨줄 때 어디선가 인코딩에 문제가 생기는 것 같아 보였다. 아니나 다를까 대시보드의 언어 설정을 영어로 하면 아무 문제 없이 잘 동작하고, 한국어 및 다른 언어로 하면 500 에러가 발생했다.
오픈스택 대시보드에서 패스워드 변경시 horizon 프로젝트에서 호출되는 코드는 아래와 같다. (openstack_dashboard/dashboards/settings/password/forms.py)
@sensitive_variables('data')
def handle(self, request, data):
user_is_editable = api.keystone.keystone_can_edit_user()
user_id = request.user.id
user = api.keystone.user_get(self.request, user_id, admin=False)
options = getattr(user, "options", {})
lock_password = options.get("lock_password", False)
if lock_password:
messages.error(request, _('Password is locked.'))
return False
if user_is_editable:
try:
api.keystone.user_update_own_password(request,
data['current_password'],
data['new_password'])
response = http.HttpResponseRedirect(settings.LOGOUT_URL)
msg = _("Password changed. Please log in again to continue.")
utils.add_logout_reason(request, response, msg)
return response
except Exception as ex:
exceptions.handle(request,
_('Unable to change password: %s') % ex)
return False
else:
messages.error(request, _('Changing password is not supported.'))
return False
문제가 발생하는 부분은 위 파일의 77번 라인인 utils.add_logout_reason(request, response, msg)
이다. 코드에서는 위 함수로 메시지를 넘겨주게 되고, 해당 코드를 따라가 보면 아래와 같다.
(horizon/utils/functions.py)
def add_logout_reason(request, response, reason, status='success'):
# Store the translated string in the cookie
lang = translation.get_language_from_request(request)
with translation.override(lang):
reason = str(reason)
response.set_cookie('logout_reason', reason, max_age=10)
response.set_cookie('logout_status', status, max_age=10)
코드에서는 사용자별로 설정해둔 언어에 따라서 전달받은 메시지를 번역하고 그 메시지를 쿠키에 담는다. 이게 에러가 발생하는 원인이 되는데, 그 이유는 쿠키에 번역된 메시지(한글)가 담기기 때문에 django에서 해석을 못 하게 되면서 500 에러가 발생하는 것으로 보인다.
메시지를 미리 인코딩해서 쿠키에 담기
메시지를 꺼내는 부분을 건드려 보기