This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/endpoints/api/logs.py
2016-01-22 15:54:06 -05:00

241 lines
9.2 KiB
Python

""" Access usage logs for organizations or repositories. """
import json
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args,
RepositoryParamResource, require_repo_admin, related_user_resource,
format_date, Unauthorized, NotFound, require_user_admin,
internal_only, path_param, require_scope)
from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user
from data import model
from auth import scopes
from app import avatar
LOGS_PER_PAGE = 50
MAX_PAGES = 20
def log_view(log, kinds):
view = {
'kind': kinds[log.kind_id],
'metadata': json.loads(log.metadata_json),
'ip': log.ip,
'datetime': format_date(log.datetime),
}
if log.performer and log.performer.username:
view['performer'] = {
'kind': 'user',
'name': log.performer.username,
'is_robot': log.performer.robot,
'avatar': avatar.get_data_for_user(log.performer)
}
return view
def aggregated_log_view(log, kinds, start_time):
# Because we aggregate based on the day of the month in SQL, we only have that information.
# Therefore, create a synthetic date based on the day and the month of the start time.
# Logs are allowed for a maximum period of one week, so this calculation should always work.
synthetic_date = datetime(start_time.year, start_time.month, int(log.day))
if synthetic_date.day < start_time.day:
synthetic_date = synthetic_date + relativedelta(months=1)
view = {
'kind': kinds[log.kind_id],
'count': log.count,
'datetime': format_date(synthetic_date),
}
return view
def _validate_logs_arguments(start_time, end_time, performer_name):
performer = None
if performer_name:
performer = model.user.get_user(performer_name)
if start_time:
try:
start_time = datetime.strptime(start_time + ' UTC', '%m/%d/%Y %Z')
except ValueError:
start_time = None
if not start_time:
start_time = datetime.today() - timedelta(7) # One week
if end_time:
try:
end_time = datetime.strptime(end_time + ' UTC', '%m/%d/%Y %Z')
end_time = end_time + timedelta(days=1)
except ValueError:
end_time = None
if not end_time:
end_time = datetime.today()
return (start_time, end_time, performer)
def get_logs(start_time, end_time, performer_name=None, repository=None, namespace=None, page=None):
(start_time, end_time, performer) = _validate_logs_arguments(start_time, end_time, performer_name)
page = min(MAX_PAGES, page if page else 1)
kinds = model.log.get_log_entry_kinds()
logs = model.log.list_logs(start_time, end_time, performer=performer, repository=repository,
namespace=namespace, page=page, count=LOGS_PER_PAGE + 1)
return {
'start_time': format_date(start_time),
'end_time': format_date(end_time),
'logs': [log_view(log, kinds) for log in logs[0:LOGS_PER_PAGE]],
'page': page,
'has_additional': len(logs) > LOGS_PER_PAGE,
}
def get_aggregate_logs(start_time, end_time, performer_name=None, repository=None, namespace=None):
(start_time, end_time, performer) = _validate_logs_arguments(start_time, end_time, performer_name)
kinds = model.log.get_log_entry_kinds()
aggregated_logs = model.log.get_aggregated_logs(start_time, end_time, performer=performer,
repository=repository, namespace=namespace)
return {
'aggregated': [aggregated_log_view(log, kinds, start_time) for log in aggregated_logs]
}
@resource('/v1/repository/<apirepopath:repository>/logs')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class RepositoryLogs(RepositoryParamResource):
""" Resource for fetching logs for the specific repository. """
@require_repo_admin
@nickname('listRepoLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs (%m/%d/%Y %Z)', type=str)
@query_param('page', 'The page number for the logs', type=int, default=1)
def get(self, args, namespace, repository):
""" List the logs for the specified repository. """
repo = model.repository.get_repository(namespace, repository)
if not repo:
raise NotFound()
start_time = args['starttime']
end_time = args['endtime']
return get_logs(start_time, end_time, repository=repo, page=args['page'])
@resource('/v1/user/logs')
@internal_only
class UserLogs(ApiResource):
""" Resource for fetching logs for the current user. """
@require_user_admin
@nickname('listUserLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('performer', 'Username for which to filter logs.', type=str)
@query_param('page', 'The page number for the logs', type=int, default=1)
def get(self, args):
""" List the logs for the current user. """
performer_name = args['performer']
start_time = args['starttime']
end_time = args['endtime']
user = get_authenticated_user()
return get_logs(start_time, end_time, performer_name=performer_name, namespace=user.username,
page=args['page'])
@resource('/v1/organization/<orgname>/logs')
@path_param('orgname', 'The name of the organization')
@related_user_resource(UserLogs)
class OrgLogs(ApiResource):
""" Resource for fetching logs for the entire organization. """
@nickname('listOrgLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('performer', 'Username for which to filter logs.', type=str)
@query_param('page', 'The page number for the logs', type=int, default=1)
@require_scope(scopes.ORG_ADMIN)
def get(self, args, orgname):
""" List the logs for the specified organization. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
performer_name = args['performer']
start_time = args['starttime']
end_time = args['endtime']
return get_logs(start_time, end_time, namespace=orgname, performer_name=performer_name,
page=args['page'])
raise Unauthorized()
@resource('/v1/repository/<apirepopath:repository>/aggregatelogs')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class RepositoryAggregateLogs(RepositoryParamResource):
""" Resource for fetching aggregated logs for the specific repository. """
@require_repo_admin
@nickname('getAggregateRepoLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs (%m/%d/%Y %Z)', type=str)
def get(self, args, namespace, repository):
""" Returns the aggregated logs for the specified repository. """
repo = model.repository.get_repository(namespace, repository)
if not repo:
raise NotFound()
start_time = args['starttime']
end_time = args['endtime']
return get_aggregate_logs(start_time, end_time, repository=repo)
@resource('/v1/user/aggregatelogs')
@internal_only
class UserAggregateLogs(ApiResource):
""" Resource for fetching aggregated logs for the current user. """
@require_user_admin
@nickname('getAggregateUserLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('performer', 'Username for which to filter logs.', type=str)
def get(self, args):
""" Returns the aggregated logs for the current user. """
performer_name = args['performer']
start_time = args['starttime']
end_time = args['endtime']
user = get_authenticated_user()
return get_aggregate_logs(start_time, end_time, performer_name=performer_name,
namespace=user.username)
@resource('/v1/organization/<orgname>/aggregatelogs')
@path_param('orgname', 'The name of the organization')
@related_user_resource(UserLogs)
class OrgAggregateLogs(ApiResource):
""" Resource for fetching aggregate logs for the entire organization. """
@nickname('getAggregateOrgLogs')
@parse_args
@query_param('starttime', 'Earliest time from which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('endtime', 'Latest time to which to get logs. (%m/%d/%Y %Z)', type=str)
@query_param('performer', 'Username for which to filter logs.', type=str)
@require_scope(scopes.ORG_ADMIN)
def get(self, args, orgname):
""" Gets the aggregated logs for the specified organization. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
performer_name = args['performer']
start_time = args['starttime']
end_time = args['endtime']
return get_aggregate_logs(start_time, end_time, namespace=orgname,
performer_name=performer_name)
raise Unauthorized()