Change string generation mechanism for info logging
As stated in Openstack logging and i18n guidelines it is recommended that formatted string for log messages should be generated by logger (not by logger client/user). In this case we can skip formatted message generation if log level configured not to show info messages. Also some non-used initialization of logger has been deleted from the code. Change-Id: I25660395417ea288aee6a70609a5336e6f74c291
This commit is contained in:
parent
e4930fd20b
commit
37fa0fca83
@ -709,7 +709,7 @@ class Controller(controller.BaseController):
|
|||||||
location_data = self._upload(req, image_meta)
|
location_data = self._upload(req, image_meta)
|
||||||
image_id = image_meta['id']
|
image_id = image_meta['id']
|
||||||
LOG.info(_LI("Uploaded data of image %s from request "
|
LOG.info(_LI("Uploaded data of image %s from request "
|
||||||
"payload successfully.") % image_id)
|
"payload successfully."), image_id)
|
||||||
|
|
||||||
if location_data:
|
if location_data:
|
||||||
try:
|
try:
|
||||||
|
@ -124,7 +124,7 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier):
|
|||||||
except exception.StorageQuotaFull:
|
except exception.StorageQuotaFull:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.info(_LI('Cleaning up %s after exceeding '
|
LOG.info(_LI('Cleaning up %s after exceeding '
|
||||||
'the quota') % image_id)
|
'the quota'), image_id)
|
||||||
store_utils.safe_delete_from_backend(
|
store_utils.safe_delete_from_backend(
|
||||||
req.context, image_meta['id'], location_data)
|
req.context, image_meta['id'], location_data)
|
||||||
|
|
||||||
@ -172,8 +172,8 @@ def upload_data_to_store(req, image_meta, image_data, store, notifier):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
except exception.ImageNotFound:
|
except exception.ImageNotFound:
|
||||||
msg = _LI("Image %s could not be found after upload. The image may"
|
msg = _("Image %s could not be found after upload. The image may"
|
||||||
" have been deleted during the upload.") % image_id
|
" have been deleted during the upload.") % image_id
|
||||||
LOG.info(msg)
|
LOG.info(msg)
|
||||||
|
|
||||||
# NOTE(jculp): we need to clean up the datastore if an image
|
# NOTE(jculp): we need to clean up the datastore if an image
|
||||||
|
@ -48,7 +48,7 @@ class ImageActionsController(object):
|
|||||||
image = image_repo.get(image_id)
|
image = image_repo.get(image_id)
|
||||||
image.deactivate()
|
image.deactivate()
|
||||||
image_repo.save(image)
|
image_repo.save(image)
|
||||||
LOG.info(_LI("Image %s is deactivated") % image_id)
|
LOG.info(_LI("Image %s is deactivated"), image_id)
|
||||||
except exception.NotFound as e:
|
except exception.NotFound as e:
|
||||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||||
except exception.Forbidden as e:
|
except exception.Forbidden as e:
|
||||||
@ -64,7 +64,7 @@ class ImageActionsController(object):
|
|||||||
image = image_repo.get(image_id)
|
image = image_repo.get(image_id)
|
||||||
image.reactivate()
|
image.reactivate()
|
||||||
image_repo.save(image)
|
image_repo.save(image)
|
||||||
LOG.info(_LI("Image %s is reactivated") % image_id)
|
LOG.info(_LI("Image %s is reactivated"), image_id)
|
||||||
except exception.NotFound as e:
|
except exception.NotFound as e:
|
||||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||||
except exception.Forbidden as e:
|
except exception.Forbidden as e:
|
||||||
|
@ -373,7 +373,7 @@ class _CompleteTask(task.Task):
|
|||||||
finally:
|
finally:
|
||||||
self.task_repo.save(task)
|
self.task_repo.save(task)
|
||||||
|
|
||||||
LOG.info(_LI("%(task_id)s of %(task_type)s completed") %
|
LOG.info(_LI("%(task_id)s of %(task_type)s completed"),
|
||||||
{'task_id': self.task_id, 'task_type': self.task_type})
|
{'task_id': self.task_id, 'task_type': self.task_type})
|
||||||
|
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ def replication_dump(options, args):
|
|||||||
|
|
||||||
data_path = os.path.join(path, image['id'])
|
data_path = os.path.join(path, image['id'])
|
||||||
if not os.path.exists(data_path):
|
if not os.path.exists(data_path):
|
||||||
LOG.info(_LI('Storing: %s') % image['id'])
|
LOG.info(_LI('Storing: %s'), image['id'])
|
||||||
|
|
||||||
# Dump glance information
|
# Dump glance information
|
||||||
with open(data_path, 'w') as f:
|
with open(data_path, 'w') as f:
|
||||||
@ -443,7 +443,7 @@ def replication_load(options, args):
|
|||||||
for ent in os.listdir(path):
|
for ent in os.listdir(path):
|
||||||
if utils.is_uuid_like(ent):
|
if utils.is_uuid_like(ent):
|
||||||
image_uuid = ent
|
image_uuid = ent
|
||||||
LOG.info(_LI('Considering: %s') % image_uuid)
|
LOG.info(_LI('Considering: %s'), image_uuid)
|
||||||
|
|
||||||
meta_file_name = os.path.join(path, image_uuid)
|
meta_file_name = os.path.join(path, image_uuid)
|
||||||
with open(meta_file_name) as meta_file:
|
with open(meta_file_name) as meta_file:
|
||||||
@ -469,8 +469,7 @@ def replication_load(options, args):
|
|||||||
del headers[key]
|
del headers[key]
|
||||||
|
|
||||||
if _dict_diff(meta, headers):
|
if _dict_diff(meta, headers):
|
||||||
LOG.info(_LI('Image %s metadata has changed') %
|
LOG.info(_LI('Image %s metadata has changed'), image_uuid)
|
||||||
image_uuid)
|
|
||||||
headers, body = client.add_image_meta(meta)
|
headers, body = client.add_image_meta(meta)
|
||||||
_check_upload_response_headers(headers, body)
|
_check_upload_response_headers(headers, body)
|
||||||
updated.append(meta['id'])
|
updated.append(meta['id'])
|
||||||
@ -544,14 +543,13 @@ def replication_livecopy(options, args):
|
|||||||
del headers[key]
|
del headers[key]
|
||||||
|
|
||||||
if _dict_diff(image, headers):
|
if _dict_diff(image, headers):
|
||||||
LOG.info(_LI('Image %s metadata has changed') %
|
LOG.info(_LI('Image %s metadata has changed'), image['id'])
|
||||||
image['id'])
|
|
||||||
headers, body = slave_client.add_image_meta(image)
|
headers, body = slave_client.add_image_meta(image)
|
||||||
_check_upload_response_headers(headers, body)
|
_check_upload_response_headers(headers, body)
|
||||||
updated.append(image['id'])
|
updated.append(image['id'])
|
||||||
|
|
||||||
elif image['status'] == 'active':
|
elif image['status'] == 'active':
|
||||||
LOG.info(_LI('Image %s is being synced') % image['id'])
|
LOG.info(_LI('Image %s is being synced'), image['id'])
|
||||||
if not options.metaonly:
|
if not options.metaonly:
|
||||||
image_response = master_client.get_image(image['id'])
|
image_response = master_client.get_image(image['id'])
|
||||||
try:
|
try:
|
||||||
|
@ -128,7 +128,7 @@ class ArtifactsPluginLoader(object):
|
|||||||
|
|
||||||
def _all_allowed(ext):
|
def _all_allowed(ext):
|
||||||
LOG.info(
|
LOG.info(
|
||||||
_LI("Artifact %s has been successfully loaded") % ext.name)
|
_LI("Artifact %s has been successfully loaded"), ext.name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if not CONF.load_enabled:
|
if not CONF.load_enabled:
|
||||||
@ -152,7 +152,7 @@ class ArtifactsPluginLoader(object):
|
|||||||
" available_plugins list") % ext.name)
|
" available_plugins list") % ext.name)
|
||||||
raise exception.ArtifactLoadError(name=ext.name)
|
raise exception.ArtifactLoadError(name=ext.name)
|
||||||
LOG.info(
|
LOG.info(
|
||||||
_LI("Artifact %s has been successfully loaded") % ext.name)
|
_LI("Artifact %s has been successfully loaded"), ext.name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return _check_ext
|
return _check_ext
|
||||||
|
@ -39,7 +39,7 @@ _LW = i18n._LW
|
|||||||
|
|
||||||
def run(t_id, context, task_repo, image_repo, image_factory):
|
def run(t_id, context, task_repo, image_repo, image_factory):
|
||||||
LOG.info(_LI('Task %(task_id)s beginning import '
|
LOG.info(_LI('Task %(task_id)s beginning import '
|
||||||
'execution.') % {'task_id': t_id})
|
'execution.'), {'task_id': t_id})
|
||||||
_execute(t_id, task_repo, image_repo, image_factory)
|
_execute(t_id, task_repo, image_repo, image_factory)
|
||||||
|
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ def set_image_data(image, uri, task_id):
|
|||||||
data_iter = None
|
data_iter = None
|
||||||
try:
|
try:
|
||||||
LOG.info(_LI("Task %(task_id)s: Got image data uri %(data_uri)s to be "
|
LOG.info(_LI("Task %(task_id)s: Got image data uri %(data_uri)s to be "
|
||||||
"imported") % {"data_uri": uri, "task_id": task_id})
|
"imported"), {"data_uri": uri, "task_id": task_id})
|
||||||
data_iter = script_utils.get_image_data_iter(uri)
|
data_iter = script_utils.get_image_data_iter(uri)
|
||||||
image.set_data(data_iter)
|
image.set_data(data_iter)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -159,8 +159,8 @@ def set_image_data(image, uri, task_id):
|
|||||||
{"error": encodeutils.exception_to_unicode(e),
|
{"error": encodeutils.exception_to_unicode(e),
|
||||||
"task_id": task_id})
|
"task_id": task_id})
|
||||||
LOG.info(_LI("Task %(task_id)s: Could not import image file"
|
LOG.info(_LI("Task %(task_id)s: Could not import image file"
|
||||||
" %(image_data)s") % {"image_data": uri,
|
" %(image_data)s"), {"image_data": uri,
|
||||||
"task_id": task_id})
|
"task_id": task_id})
|
||||||
finally:
|
finally:
|
||||||
if isinstance(data_iter, file):
|
if isinstance(data_iter, file):
|
||||||
data_iter.close()
|
data_iter.close()
|
||||||
|
@ -308,7 +308,7 @@ class Server(object):
|
|||||||
self.pool.spawn_n(self._single_run, self.application, self.sock)
|
self.pool.spawn_n(self._single_run, self.application, self.sock)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
LOG.info(_LI("Starting %d workers") % CONF.workers)
|
LOG.info(_LI("Starting %d workers"), CONF.workers)
|
||||||
signal.signal(signal.SIGTERM, self.kill_children)
|
signal.signal(signal.SIGTERM, self.kill_children)
|
||||||
signal.signal(signal.SIGINT, self.kill_children)
|
signal.signal(signal.SIGINT, self.kill_children)
|
||||||
signal.signal(signal.SIGHUP, self.hup)
|
signal.signal(signal.SIGHUP, self.hup)
|
||||||
@ -321,10 +321,10 @@ class Server(object):
|
|||||||
def _remove_children(self, pid):
|
def _remove_children(self, pid):
|
||||||
if pid in self.children:
|
if pid in self.children:
|
||||||
self.children.remove(pid)
|
self.children.remove(pid)
|
||||||
LOG.info(_LI('Removed dead child %s') % pid)
|
LOG.info(_LI('Removed dead child %s'), pid)
|
||||||
elif pid in self.stale_children:
|
elif pid in self.stale_children:
|
||||||
self.stale_children.remove(pid)
|
self.stale_children.remove(pid)
|
||||||
LOG.info(_LI('Removed stale child %s') % pid)
|
LOG.info(_LI('Removed stale child %s'), pid)
|
||||||
else:
|
else:
|
||||||
LOG.warn(_LW('Unrecognised child %s') % pid)
|
LOG.warn(_LW('Unrecognised child %s') % pid)
|
||||||
|
|
||||||
@ -433,12 +433,12 @@ class Server(object):
|
|||||||
# exit on sighup
|
# exit on sighup
|
||||||
self._sock = None
|
self._sock = None
|
||||||
self.run_server()
|
self.run_server()
|
||||||
LOG.info(_LI('Child %d exiting normally') % os.getpid())
|
LOG.info(_LI('Child %d exiting normally'), os.getpid())
|
||||||
# self.pool.waitall() is now called in wsgi's server so
|
# self.pool.waitall() is now called in wsgi's server so
|
||||||
# it's safe to exit here
|
# it's safe to exit here
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
LOG.info(_LI('Started child %s') % pid)
|
LOG.info(_LI('Started child %s'), pid)
|
||||||
self.children.add(pid)
|
self.children.add(pid)
|
||||||
|
|
||||||
def run_server(self):
|
def run_server(self):
|
||||||
|
@ -60,12 +60,12 @@ def log_call(func):
|
|||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapped(*args, **kwargs):
|
def wrapped(*args, **kwargs):
|
||||||
LOG.info(_LI('Calling %(funcname)s: args=%(args)s, '
|
LOG.info(_LI('Calling %(funcname)s: args=%(args)s, '
|
||||||
'kwargs=%(kwargs)s') %
|
'kwargs=%(kwargs)s'),
|
||||||
{"funcname": func.__name__,
|
{"funcname": func.__name__,
|
||||||
"args": args,
|
"args": args,
|
||||||
"kwargs": kwargs})
|
"kwargs": kwargs})
|
||||||
output = func(*args, **kwargs)
|
output = func(*args, **kwargs)
|
||||||
LOG.info(_LI('Returning %(funcname)s: %(output)s') %
|
LOG.info(_LI('Returning %(funcname)s: %(output)s'),
|
||||||
{"funcname": func.__name__,
|
{"funcname": func.__name__,
|
||||||
"output": output})
|
"output": output})
|
||||||
return output
|
return output
|
||||||
@ -2000,7 +2000,7 @@ def _artifact_get(context, artifact_id, type_name,
|
|||||||
artifact['type_version'] != type_version)):
|
artifact['type_version'] != type_version)):
|
||||||
raise KeyError
|
raise KeyError
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOG.info(_LI('Could not find artifact %s') % artifact_id)
|
LOG.info(_LI('Could not find artifact %s'), artifact_id)
|
||||||
raise exception.NotFound()
|
raise exception.NotFound()
|
||||||
|
|
||||||
if artifact['deleted_at']:
|
if artifact['deleted_at']:
|
||||||
|
@ -450,7 +450,7 @@ def _export_data_to_file(meta, path):
|
|||||||
json_file.write(json.dumps(values))
|
json_file.write(json.dumps(values))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(encodeutils.exception_to_unicode(e))
|
LOG.exception(encodeutils.exception_to_unicode(e))
|
||||||
LOG.info(_LI("Namespace %(namespace)s saved in %(file)s") % {
|
LOG.info(_LI("Namespace %(namespace)s saved in %(file)s"), {
|
||||||
'namespace': namespace_file_name, 'file': file_name})
|
'namespace': namespace_file_name, 'file': file_name})
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,11 +98,11 @@ def from_migration_import(module_name, fromlist):
|
|||||||
|
|
||||||
def create_tables(tables):
|
def create_tables(tables):
|
||||||
for table in tables:
|
for table in tables:
|
||||||
LOG.info(_LI("creating table %(table)s") % {'table': table})
|
LOG.info(_LI("creating table %(table)s"), {'table': table})
|
||||||
table.create()
|
table.create()
|
||||||
|
|
||||||
|
|
||||||
def drop_tables(tables):
|
def drop_tables(tables):
|
||||||
for table in tables:
|
for table in tables:
|
||||||
LOG.info(_LI("dropping table %(table)s") % {'table': table})
|
LOG.info(_LI("dropping table %(table)s"), {'table': table})
|
||||||
table.drop()
|
table.drop()
|
||||||
|
@ -403,18 +403,16 @@ class Task(object):
|
|||||||
def _set_task_status(self, new_status):
|
def _set_task_status(self, new_status):
|
||||||
if self._validate_task_status_transition(self.status, new_status):
|
if self._validate_task_status_transition(self.status, new_status):
|
||||||
self._status = new_status
|
self._status = new_status
|
||||||
log_msg = (_LI("Task [%(task_id)s] status changing from "
|
LOG.info(_LI("Task [%(task_id)s] status changing from "
|
||||||
"%(cur_status)s to %(new_status)s") %
|
"%(cur_status)s to %(new_status)s"),
|
||||||
{'task_id': self.task_id, 'cur_status': self.status,
|
{'task_id': self.task_id, 'cur_status': self.status,
|
||||||
'new_status': new_status})
|
'new_status': new_status})
|
||||||
LOG.info(log_msg)
|
|
||||||
self._status = new_status
|
self._status = new_status
|
||||||
else:
|
else:
|
||||||
log_msg = (_LE("Task [%(task_id)s] status failed to change from "
|
LOG.error(_LE("Task [%(task_id)s] status failed to change from "
|
||||||
"%(cur_status)s to %(new_status)s") %
|
"%(cur_status)s to %(new_status)s"),
|
||||||
{'task_id': self.task_id, 'cur_status': self.status,
|
{'task_id': self.task_id, 'cur_status': self.status,
|
||||||
'new_status': new_status})
|
'new_status': new_status})
|
||||||
LOG.error(log_msg)
|
|
||||||
raise exception.InvalidTaskStatusTransition(
|
raise exception.InvalidTaskStatusTransition(
|
||||||
cur_status=self.status,
|
cur_status=self.status,
|
||||||
new_status=new_status
|
new_status=new_status
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
import glance_store
|
import glance_store
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from glance.api import authorization
|
from glance.api import authorization
|
||||||
from glance.api import policy
|
from glance.api import policy
|
||||||
@ -28,9 +27,6 @@ import glance.notifier
|
|||||||
import glance.quota
|
import glance.quota
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Gateway(object):
|
class Gateway(object):
|
||||||
def __init__(self, db_api=None, store_api=None, notifier=None,
|
def __init__(self, db_api=None, store_api=None, notifier=None,
|
||||||
policy_enforcer=None):
|
policy_enforcer=None):
|
||||||
|
@ -69,8 +69,7 @@ class ImageCache(object):
|
|||||||
driver_module = (__name__ + '.drivers.' + driver_name + '.Driver')
|
driver_module = (__name__ + '.drivers.' + driver_name + '.Driver')
|
||||||
try:
|
try:
|
||||||
self.driver_class = importutils.import_class(driver_module)
|
self.driver_class = importutils.import_class(driver_module)
|
||||||
LOG.info(_LI("Image cache loaded driver '%s'.") %
|
LOG.info(_LI("Image cache loaded driver '%s'."), driver_name)
|
||||||
driver_name)
|
|
||||||
except ImportError as import_err:
|
except ImportError as import_err:
|
||||||
LOG.warn(_LW("Image cache driver "
|
LOG.warn(_LW("Image cache driver "
|
||||||
"'%(driver_name)s' failed to load. "
|
"'%(driver_name)s' failed to load. "
|
||||||
|
@ -410,19 +410,16 @@ class Driver(base.Driver):
|
|||||||
:param image_id: Image ID
|
:param image_id: Image ID
|
||||||
"""
|
"""
|
||||||
if self.is_cached(image_id):
|
if self.is_cached(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already cached.") % image_id
|
LOG.info(_LI("Not queueing image '%s'. Already cached."), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.is_being_cached(image_id):
|
if self.is_being_cached(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already being "
|
LOG.info(_LI("Not queueing image '%s'. Already being "
|
||||||
"written to cache") % image_id
|
"written to cache"), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.is_queued(image_id):
|
if self.is_queued(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already queued.") % image_id
|
LOG.info(_LI("Not queueing image '%s'. Already queued."), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
path = self.get_image_filepath(image_id, 'queue')
|
path = self.get_image_filepath(image_id, 'queue')
|
||||||
@ -439,7 +436,7 @@ class Driver(base.Driver):
|
|||||||
"""
|
"""
|
||||||
for path in self.get_cache_files(self.invalid_dir):
|
for path in self.get_cache_files(self.invalid_dir):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
LOG.info(_LI("Removed invalid cache file %s") % path)
|
LOG.info(_LI("Removed invalid cache file %s"), path)
|
||||||
|
|
||||||
def delete_stalled_files(self, older_than):
|
def delete_stalled_files(self, older_than):
|
||||||
"""
|
"""
|
||||||
@ -453,7 +450,7 @@ class Driver(base.Driver):
|
|||||||
if os.path.getmtime(path) < older_than:
|
if os.path.getmtime(path) < older_than:
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
LOG.info(_LI("Removed stalled cache file %s") % path)
|
LOG.info(_LI("Removed stalled cache file %s"), path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = (_LW("Failed to delete file %(path)s. "
|
msg = (_LW("Failed to delete file %(path)s. "
|
||||||
"Got error: %(e)s"),
|
"Got error: %(e)s"),
|
||||||
|
@ -338,19 +338,16 @@ class Driver(base.Driver):
|
|||||||
:param image_id: Image ID
|
:param image_id: Image ID
|
||||||
"""
|
"""
|
||||||
if self.is_cached(image_id):
|
if self.is_cached(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already cached.") % image_id
|
LOG.info(_LI("Not queueing image '%s'. Already cached."), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.is_being_cached(image_id):
|
if self.is_being_cached(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already being "
|
LOG.info(_LI("Not queueing image '%s'. Already being "
|
||||||
"written to cache") % image_id
|
"written to cache"), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.is_queued(image_id):
|
if self.is_queued(image_id):
|
||||||
msg = _LI("Not queueing image '%s'. Already queued.") % image_id
|
LOG.info(_LI("Not queueing image '%s'. Already queued."), image_id)
|
||||||
LOG.info(msg)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
path = self.get_image_filepath(image_id, 'queue')
|
path = self.get_image_filepath(image_id, 'queue')
|
||||||
|
@ -82,5 +82,5 @@ class Prefetcher(base.CacheApp):
|
|||||||
"images in queue."))
|
"images in queue."))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
LOG.info(_LI("Successfully cached all %d images") % num_images)
|
LOG.info(_LI("Successfully cached all %d images"), num_images)
|
||||||
return True
|
return True
|
||||||
|
@ -385,9 +385,8 @@ class ImageProxy(glance.domain.proxy.Image):
|
|||||||
result = signature_utils.verify_signature(
|
result = signature_utils.verify_signature(
|
||||||
self.context, checksum, self.image.extra_properties)
|
self.context, checksum, self.image.extra_properties)
|
||||||
if result:
|
if result:
|
||||||
msg = (_LI("Successfully verified signature for image "
|
LOG.info(_LI("Successfully verified signature for image %s"),
|
||||||
"%s") % self.image.image_id)
|
self.image.image_id)
|
||||||
LOG.info(msg)
|
|
||||||
|
|
||||||
self.image.locations = [{'url': location, 'metadata': loc_meta,
|
self.image.locations = [{'url': location, 'metadata': loc_meta,
|
||||||
'status': 'active'}]
|
'status': 'active'}]
|
||||||
|
@ -327,8 +327,8 @@ class ImageProxy(glance.domain.proxy.Image):
|
|||||||
image_id=self.image.image_id)
|
image_id=self.image.image_id)
|
||||||
except exception.StorageQuotaFull:
|
except exception.StorageQuotaFull:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.info(_LI('Cleaning up %s after exceeding the quota.')
|
LOG.info(_LI('Cleaning up %s after exceeding the quota.'),
|
||||||
% self.image.image_id)
|
self.image.image_id)
|
||||||
self.store_utils.safe_delete_from_backend(
|
self.store_utils.safe_delete_from_backend(
|
||||||
self.context, self.image.image_id, self.image.locations[0])
|
self.context, self.image.image_id, self.image.locations[0])
|
||||||
|
|
||||||
|
@ -341,15 +341,13 @@ class Controller(object):
|
|||||||
msg = "Successfully retrieved image %(id)s" % {'id': id}
|
msg = "Successfully retrieved image %(id)s" % {'id': id}
|
||||||
LOG.debug(msg)
|
LOG.debug(msg)
|
||||||
except exception.ImageNotFound:
|
except exception.ImageNotFound:
|
||||||
msg = _LI("Image %(id)s not found") % {'id': id}
|
LOG.info(_LI("Image %(id)s not found"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPNotFound()
|
raise exc.HTTPNotFound()
|
||||||
except exception.Forbidden:
|
except exception.Forbidden:
|
||||||
# If it's private and doesn't belong to them, don't let on
|
# If it's private and doesn't belong to them, don't let on
|
||||||
# that it exists
|
# that it exists
|
||||||
msg = _LI("Access denied to image %(id)s but returning"
|
LOG.info(_LI("Access denied to image %(id)s but returning"
|
||||||
" 'not found'") % {'id': id}
|
" 'not found'"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPNotFound()
|
raise exc.HTTPNotFound()
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_LE("Unable to show image %s") % id)
|
LOG.exception(_LE("Unable to show image %s") % id)
|
||||||
@ -369,23 +367,19 @@ class Controller(object):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
deleted_image = self.db_api.image_destroy(req.context, id)
|
deleted_image = self.db_api.image_destroy(req.context, id)
|
||||||
msg = _LI("Successfully deleted image %(id)s") % {'id': id}
|
LOG.info(_LI("Successfully deleted image %(id)s"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
return dict(image=make_image_dict(deleted_image))
|
return dict(image=make_image_dict(deleted_image))
|
||||||
except exception.ForbiddenPublicImage:
|
except exception.ForbiddenPublicImage:
|
||||||
msg = _LI("Delete denied for public image %(id)s") % {'id': id}
|
LOG.info(_LI("Delete denied for public image %(id)s"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPForbidden()
|
raise exc.HTTPForbidden()
|
||||||
except exception.Forbidden:
|
except exception.Forbidden:
|
||||||
# If it's private and doesn't belong to them, don't let on
|
# If it's private and doesn't belong to them, don't let on
|
||||||
# that it exists
|
# that it exists
|
||||||
msg = _LI("Access denied to image %(id)s but returning"
|
LOG.info(_LI("Access denied to image %(id)s but returning"
|
||||||
" 'not found'") % {'id': id}
|
" 'not found'"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
return exc.HTTPNotFound()
|
return exc.HTTPNotFound()
|
||||||
except exception.ImageNotFound:
|
except exception.ImageNotFound:
|
||||||
msg = _LI("Image %(id)s not found") % {'id': id}
|
LOG.info(_LI("Image %(id)s not found"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
return exc.HTTPNotFound()
|
return exc.HTTPNotFound()
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_LE("Unable to delete image %s") % id)
|
LOG.exception(_LE("Unable to delete image %s") % id)
|
||||||
@ -413,9 +407,8 @@ class Controller(object):
|
|||||||
|
|
||||||
image_id = image_data.get('id')
|
image_id = image_data.get('id')
|
||||||
if image_id and not utils.is_uuid_like(image_id):
|
if image_id and not utils.is_uuid_like(image_id):
|
||||||
msg = _LI("Rejecting image creation request for invalid image "
|
LOG.info(_LI("Rejecting image creation request for invalid image "
|
||||||
"id '%(bad_id)s'") % {'bad_id': image_id}
|
"id '%(bad_id)s'"), {'bad_id': image_id})
|
||||||
LOG.info(msg)
|
|
||||||
msg = _("Invalid image id format")
|
msg = _("Invalid image id format")
|
||||||
return exc.HTTPBadRequest(explanation=msg)
|
return exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
@ -426,9 +419,8 @@ class Controller(object):
|
|||||||
image_data = _normalize_image_location_for_db(image_data)
|
image_data = _normalize_image_location_for_db(image_data)
|
||||||
image_data = self.db_api.image_create(req.context, image_data)
|
image_data = self.db_api.image_create(req.context, image_data)
|
||||||
image_data = dict(image=make_image_dict(image_data))
|
image_data = dict(image=make_image_dict(image_data))
|
||||||
msg = (_LI("Successfully created image %(id)s") %
|
LOG.info(_LI("Successfully created image %(id)s"),
|
||||||
image_data['image'])
|
{'id': image_data['image']['id']})
|
||||||
LOG.info(msg)
|
|
||||||
return image_data
|
return image_data
|
||||||
except exception.Duplicate:
|
except exception.Duplicate:
|
||||||
msg = _("Image with identifier %s already exists!") % image_id
|
msg = _("Image with identifier %s already exists!") % image_id
|
||||||
@ -480,8 +472,7 @@ class Controller(object):
|
|||||||
purge_props=purge_props,
|
purge_props=purge_props,
|
||||||
from_state=from_state)
|
from_state=from_state)
|
||||||
|
|
||||||
msg = _LI("Updating metadata for image %(id)s") % {'id': id}
|
LOG.info(_LI("Updating metadata for image %(id)s"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
return dict(image=make_image_dict(updated_image))
|
return dict(image=make_image_dict(updated_image))
|
||||||
except exception.Invalid as e:
|
except exception.Invalid as e:
|
||||||
msg = (_("Failed to update image metadata. "
|
msg = (_("Failed to update image metadata. "
|
||||||
@ -489,21 +480,18 @@ class Controller(object):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
return exc.HTTPBadRequest(msg)
|
return exc.HTTPBadRequest(msg)
|
||||||
except exception.ImageNotFound:
|
except exception.ImageNotFound:
|
||||||
msg = _LI("Image %(id)s not found") % {'id': id}
|
LOG.info(_LI("Image %(id)s not found"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPNotFound(body='Image not found',
|
raise exc.HTTPNotFound(body='Image not found',
|
||||||
request=req,
|
request=req,
|
||||||
content_type='text/plain')
|
content_type='text/plain')
|
||||||
except exception.ForbiddenPublicImage:
|
except exception.ForbiddenPublicImage:
|
||||||
msg = _LI("Update denied for public image %(id)s") % {'id': id}
|
LOG.info(_LI("Update denied for public image %(id)s"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPForbidden()
|
raise exc.HTTPForbidden()
|
||||||
except exception.Forbidden:
|
except exception.Forbidden:
|
||||||
# If it's private and doesn't belong to them, don't let on
|
# If it's private and doesn't belong to them, don't let on
|
||||||
# that it exists
|
# that it exists
|
||||||
msg = _LI("Access denied to image %(id)s but returning"
|
LOG.info(_LI("Access denied to image %(id)s but returning"
|
||||||
" 'not found'") % {'id': id}
|
" 'not found'"), {'id': id})
|
||||||
LOG.info(msg)
|
|
||||||
raise exc.HTTPNotFound(body='Image not found',
|
raise exc.HTTPNotFound(body='Image not found',
|
||||||
request=req,
|
request=req,
|
||||||
content_type='text/plain')
|
content_type='text/plain')
|
||||||
|
@ -80,8 +80,7 @@ class Controller(object):
|
|||||||
raise webob.exc.HTTPNotFound()
|
raise webob.exc.HTTPNotFound()
|
||||||
|
|
||||||
members = self.db_api.image_member_find(req.context, image_id=image_id)
|
members = self.db_api.image_member_find(req.context, image_id=image_id)
|
||||||
msg = "Returning member list for image %(id)s" % {'id': image_id}
|
LOG.debug("Returning member list for image %(id)s", {'id': image_id})
|
||||||
LOG.debug(msg)
|
|
||||||
return dict(members=make_member_list(members,
|
return dict(members=make_member_list(members,
|
||||||
member_id='member',
|
member_id='member',
|
||||||
can_share='can_share'))
|
can_share='can_share'))
|
||||||
@ -197,9 +196,8 @@ class Controller(object):
|
|||||||
self.db_api.image_member_create(req.context, memb)
|
self.db_api.image_member_create(req.context, memb)
|
||||||
|
|
||||||
# Make an appropriate result
|
# Make an appropriate result
|
||||||
msg = (_LI("Successfully updated memberships for image %(id)s") %
|
LOG.info(_LI("Successfully updated memberships for image %(id)s"),
|
||||||
{'id': image_id})
|
{'id': image_id})
|
||||||
LOG.info(msg)
|
|
||||||
return webob.exc.HTTPNoContent()
|
return webob.exc.HTTPNoContent()
|
||||||
|
|
||||||
@utils.mutating
|
@utils.mutating
|
||||||
@ -271,9 +269,8 @@ class Controller(object):
|
|||||||
can_share=bool(can_share))
|
can_share=bool(can_share))
|
||||||
self.db_api.image_member_create(req.context, values)
|
self.db_api.image_member_create(req.context, values)
|
||||||
|
|
||||||
msg = (_LI("Successfully updated a membership for image %(id)s") %
|
LOG.info(_LI("Successfully updated a membership for image %(id)s"),
|
||||||
{'id': image_id})
|
{'id': image_id})
|
||||||
LOG.info(msg)
|
|
||||||
return webob.exc.HTTPNoContent()
|
return webob.exc.HTTPNoContent()
|
||||||
|
|
||||||
@utils.mutating
|
@utils.mutating
|
||||||
@ -313,21 +310,19 @@ class Controller(object):
|
|||||||
if members:
|
if members:
|
||||||
self.db_api.image_member_delete(req.context, members[0]['id'])
|
self.db_api.image_member_delete(req.context, members[0]['id'])
|
||||||
else:
|
else:
|
||||||
msg = ("%(id)s is not a member of image %(image_id)s" %
|
LOG.debug("%(id)s is not a member of image %(image_id)s",
|
||||||
{'id': id, 'image_id': image_id})
|
{'id': id, 'image_id': image_id})
|
||||||
LOG.debug(msg)
|
|
||||||
msg = _("Membership could not be found.")
|
msg = _("Membership could not be found.")
|
||||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||||
|
|
||||||
# Make an appropriate result
|
# Make an appropriate result
|
||||||
msg = (_LI("Successfully deleted a membership from image %(id)s") %
|
LOG.info(_LI("Successfully deleted a membership from image %(id)s"),
|
||||||
{'id': image_id})
|
{'id': image_id})
|
||||||
LOG.info(msg)
|
|
||||||
return webob.exc.HTTPNoContent()
|
return webob.exc.HTTPNoContent()
|
||||||
|
|
||||||
def default(self, req, *args, **kwargs):
|
def default(self, req, *args, **kwargs):
|
||||||
"""This will cover the missing 'show' and 'create' actions"""
|
"""This will cover the missing 'show' and 'create' actions"""
|
||||||
LOG.debug("The method %s is not allowed for this resource" %
|
LOG.debug("The method %s is not allowed for this resource",
|
||||||
req.environ['REQUEST_METHOD'])
|
req.environ['REQUEST_METHOD'])
|
||||||
raise webob.exc.HTTPMethodNotAllowed(
|
raise webob.exc.HTTPMethodNotAllowed(
|
||||||
headers=[('Allow', 'PUT, DELETE')])
|
headers=[('Allow', 'PUT, DELETE')])
|
||||||
@ -344,8 +339,8 @@ class Controller(object):
|
|||||||
msg = _("Membership could not be found.")
|
msg = _("Membership could not be found.")
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
msg = "Returning list of images shared with member %(id)s" % {'id': id}
|
LOG.debug("Returning list of images shared with member %(id)s",
|
||||||
LOG.debug(msg)
|
{'id': id})
|
||||||
return dict(shared_images=make_member_list(members,
|
return dict(shared_images=make_member_list(members,
|
||||||
image_id='image_id',
|
image_id='image_id',
|
||||||
can_share='can_share'))
|
can_share='can_share'))
|
||||||
|
@ -18,15 +18,12 @@ RPC Controller
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from glance.common import rpc
|
from glance.common import rpc
|
||||||
from glance.common import wsgi
|
from glance.common import wsgi
|
||||||
import glance.db
|
import glance.db
|
||||||
from glance import i18n
|
from glance import i18n
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
_ = i18n._
|
_ = i18n._
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -18,12 +18,8 @@ Simple client class to speak with any RESTful service that implements
|
|||||||
the Glance Registry API
|
the Glance Registry API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
|
|
||||||
from glance.common import rpc
|
from glance.common import rpc
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class RegistryClient(rpc.RPCClient):
|
class RegistryClient(rpc.RPCClient):
|
||||||
"""Registry's V2 Client."""
|
"""Registry's V2 Client."""
|
||||||
|
@ -240,7 +240,7 @@ class Daemon(object):
|
|||||||
|
|
||||||
class Scrubber(object):
|
class Scrubber(object):
|
||||||
def __init__(self, store_api):
|
def __init__(self, store_api):
|
||||||
LOG.info(_LI("Initializing scrubber with configuration: %s") %
|
LOG.info(_LI("Initializing scrubber with configuration: %s"),
|
||||||
six.text_type({'registry_host': CONF.registry_host,
|
six.text_type({'registry_host': CONF.registry_host,
|
||||||
'registry_port': CONF.registry_port}))
|
'registry_port': CONF.registry_port}))
|
||||||
|
|
||||||
@ -298,7 +298,7 @@ class Scrubber(object):
|
|||||||
if len(delete_jobs) == 0:
|
if len(delete_jobs) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
LOG.info(_LI("Scrubbing image %(id)s from %(count)d locations.") %
|
LOG.info(_LI("Scrubbing image %(id)s from %(count)d locations."),
|
||||||
{'id': image_id, 'count': len(delete_jobs)})
|
{'id': image_id, 'count': len(delete_jobs)})
|
||||||
|
|
||||||
success = True
|
success = True
|
||||||
@ -312,7 +312,7 @@ class Scrubber(object):
|
|||||||
image = self.registry.get_image(image_id)
|
image = self.registry.get_image(image_id)
|
||||||
if image['status'] == 'pending_delete':
|
if image['status'] == 'pending_delete':
|
||||||
self.registry.update_image(image_id, {'status': 'deleted'})
|
self.registry.update_image(image_id, {'status': 'deleted'})
|
||||||
LOG.info(_LI("Image %s has been scrubbed successfully") % image_id)
|
LOG.info(_LI("Image %s has been scrubbed successfully"), image_id)
|
||||||
else:
|
else:
|
||||||
LOG.warn(_LW("One or more image locations couldn't be scrubbed "
|
LOG.warn(_LW("One or more image locations couldn't be scrubbed "
|
||||||
"from backend. Leaving image '%s' in 'pending_delete'"
|
"from backend. Leaving image '%s' in 'pending_delete'"
|
||||||
@ -328,14 +328,14 @@ class Scrubber(object):
|
|||||||
except store_exceptions.NotFound:
|
except store_exceptions.NotFound:
|
||||||
LOG.info(_LI("Image location for image '%s' not found in "
|
LOG.info(_LI("Image location for image '%s' not found in "
|
||||||
"backend; Marking image location deleted in "
|
"backend; Marking image location deleted in "
|
||||||
"db.") % image_id)
|
"db."), image_id)
|
||||||
|
|
||||||
if loc_id != '-':
|
if loc_id != '-':
|
||||||
db_api.get_api().image_location_delete(self.admin_context,
|
db_api.get_api().image_location_delete(self.admin_context,
|
||||||
image_id,
|
image_id,
|
||||||
int(loc_id),
|
int(loc_id),
|
||||||
'deleted')
|
'deleted')
|
||||||
LOG.info(_LI("Image %s is scrubbed from a location.") % image_id)
|
LOG.info(_LI("Image %s is scrubbed from a location."), image_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_LE("Unable to scrub image %(id)s from a location. "
|
LOG.error(_LE("Unable to scrub image %(id)s from a location. "
|
||||||
"Reason: %(exc)s ") %
|
"Reason: %(exc)s ") %
|
||||||
|
@ -45,7 +45,6 @@ UUID2 = _gen_uuid()
|
|||||||
|
|
||||||
|
|
||||||
class TestRegistryAPI(base.IsolatedUnitTest, test_utils.RegistryAPIMixIn):
|
class TestRegistryAPI(base.IsolatedUnitTest, test_utils.RegistryAPIMixIn):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Establish a clean test environment"""
|
"""Establish a clean test environment"""
|
||||||
super(TestRegistryAPI, self).setUp()
|
super(TestRegistryAPI, self).setUp()
|
||||||
@ -1285,10 +1284,11 @@ class TestRegistryAPI(base.IsolatedUnitTest, test_utils.RegistryAPIMixIn):
|
|||||||
id='0564c64c-3545-4e34-abfb-9d18e5f2f2f9')
|
id='0564c64c-3545-4e34-abfb-9d18e5f2f2f9')
|
||||||
self.log_image_id = False
|
self.log_image_id = False
|
||||||
|
|
||||||
def fake_log_info(msg):
|
def fake_log_info(msg, image_data):
|
||||||
if ('Successfully created image '
|
if ('0564c64c-3545-4e34-abfb-9d18e5f2f2f9' == image_data['id'] and
|
||||||
'0564c64c-3545-4e34-abfb-9d18e5f2f2f9' in msg):
|
'Successfully created image' in msg):
|
||||||
self.log_image_id = True
|
self.log_image_id = True
|
||||||
|
|
||||||
self.stubs.Set(rserver.images.LOG, 'info', fake_log_info)
|
self.stubs.Set(rserver.images.LOG, 'info', fake_log_info)
|
||||||
|
|
||||||
self.get_api_response_ext(200, content_type='json', method='POST',
|
self.get_api_response_ext(200, content_type='json', method='POST',
|
||||||
@ -1837,7 +1837,6 @@ class TestRegistryAPI(base.IsolatedUnitTest, test_utils.RegistryAPIMixIn):
|
|||||||
|
|
||||||
class TestRegistryAPILocations(base.IsolatedUnitTest,
|
class TestRegistryAPILocations(base.IsolatedUnitTest,
|
||||||
test_utils.RegistryAPIMixIn):
|
test_utils.RegistryAPIMixIn):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Establish a clean test environment"""
|
"""Establish a clean test environment"""
|
||||||
super(TestRegistryAPILocations, self).setUp()
|
super(TestRegistryAPILocations, self).setUp()
|
||||||
@ -1961,7 +1960,6 @@ class TestRegistryAPILocations(base.IsolatedUnitTest,
|
|||||||
|
|
||||||
|
|
||||||
class TestSharability(test_utils.BaseTestCase):
|
class TestSharability(test_utils.BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestSharability, self).setUp()
|
super(TestSharability, self).setUp()
|
||||||
self.setup_db()
|
self.setup_db()
|
||||||
|
@ -64,9 +64,9 @@ def build_image_owner_map(owner_map, db, context):
|
|||||||
|
|
||||||
image_owner_map[image_id] = owner_id
|
image_owner_map[image_id] = owner_id
|
||||||
|
|
||||||
msg = (_LI('Image "%(image)s" owner "%(owner)s" -> "%(owner_id)s"'),
|
LOG.info(_LI('Image "%(image)s" owner "%(owner)s" -> "%(owner_id)s"'),
|
||||||
{'image': image_id, 'owner': owner_name, 'owner_id': owner_id})
|
{'image': image_id, 'owner': owner_name,
|
||||||
LOG.info(msg)
|
'owner_id': owner_id})
|
||||||
|
|
||||||
return image_owner_map
|
return image_owner_map
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ def build_image_owner_map(owner_map, db, context):
|
|||||||
def update_image_owners(image_owner_map, db, context):
|
def update_image_owners(image_owner_map, db, context):
|
||||||
for (image_id, image_owner) in image_owner_map.items():
|
for (image_id, image_owner) in image_owner_map.items():
|
||||||
db.image_update(context, image_id, {'owner': image_owner})
|
db.image_update(context, image_id, {'owner': image_owner})
|
||||||
LOG.info(_LI('Image %s successfully updated.') % image_id)
|
LOG.info(_LI('Image %s successfully updated.'), image_id)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
Reference in New Issue
Block a user