8d9f3309aa
Fixes #1246
242 lines
9.5 KiB
Python
242 lines
9.5 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,
|
|
path_param, require_scope, page_support)
|
|
from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission
|
|
from auth.auth_context import get_authenticated_user
|
|
from data import model, database
|
|
from auth import scopes
|
|
from app import avatar
|
|
|
|
LOGS_PER_PAGE = 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_token=None):
|
|
(start_time, end_time, performer) = _validate_logs_arguments(start_time, end_time, performer_name)
|
|
kinds = model.log.get_log_entry_kinds()
|
|
logs_query = model.log.get_logs_query(start_time, end_time, performer=performer,
|
|
repository=repository, namespace=namespace)
|
|
|
|
logs, next_page_token = model.modelutil.paginate(logs_query, database.LogEntry, descending=True,
|
|
page_token=page_token, limit=LOGS_PER_PAGE)
|
|
|
|
return {
|
|
'start_time': format_date(start_time),
|
|
'end_time': format_date(end_time),
|
|
'logs': [log_view(log, kinds) for log in logs],
|
|
}, next_page_token
|
|
|
|
|
|
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)
|
|
@page_support()
|
|
def get(self, namespace, repository, page_token, parsed_args):
|
|
""" List the logs for the specified repository. """
|
|
repo = model.repository.get_repository(namespace, repository)
|
|
if not repo:
|
|
raise NotFound()
|
|
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_args['endtime']
|
|
return get_logs(start_time, end_time, repository=repo, page_token=page_token)
|
|
|
|
|
|
@resource('/v1/user/logs')
|
|
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)
|
|
@page_support()
|
|
def get(self, parsed_args, page_token):
|
|
""" List the logs for the current user. """
|
|
performer_name = parsed_args['performer']
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_args['endtime']
|
|
|
|
user = get_authenticated_user()
|
|
return get_logs(start_time, end_time, performer_name=performer_name, namespace=user.username,
|
|
page_token=page_token)
|
|
|
|
|
|
@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)
|
|
@page_support()
|
|
@require_scope(scopes.ORG_ADMIN)
|
|
def get(self, orgname, page_token, parsed_args):
|
|
""" List the logs for the specified organization. """
|
|
permission = AdministerOrganizationPermission(orgname)
|
|
if permission.can():
|
|
performer_name = parsed_args['performer']
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_args['endtime']
|
|
|
|
return get_logs(start_time, end_time, namespace=orgname, performer_name=performer_name,
|
|
page_token=page_token)
|
|
|
|
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, namespace, repository, parsed_args):
|
|
""" Returns the aggregated logs for the specified repository. """
|
|
repo = model.repository.get_repository(namespace, repository)
|
|
if not repo:
|
|
raise NotFound()
|
|
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_args['endtime']
|
|
return get_aggregate_logs(start_time, end_time, repository=repo)
|
|
|
|
|
|
@resource('/v1/user/aggregatelogs')
|
|
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, parsed_args):
|
|
""" Returns the aggregated logs for the current user. """
|
|
performer_name = parsed_args['performer']
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_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, orgname, parsed_args):
|
|
""" Gets the aggregated logs for the specified organization. """
|
|
permission = AdministerOrganizationPermission(orgname)
|
|
if permission.can():
|
|
performer_name = parsed_args['performer']
|
|
start_time = parsed_args['starttime']
|
|
end_time = parsed_args['endtime']
|
|
|
|
return get_aggregate_logs(start_time, end_time, namespace=orgname,
|
|
performer_name=performer_name)
|
|
|
|
raise Unauthorized()
|