11
from datetime import datetime
11
12
from operator import attrgetter
13
from textwrap import dedent
13
15
from canonical.database.sqlbase import connect, sqlvalues
14
16
from canonical.launchpad.scripts import db_options
24
def get_where_clause(options):
25
"Generate a WHERE clause referencing the date_created column."
26
# We have two of the from timestamp, the until timestamp and an
27
# interval. The interval is in a format unsuitable for processing in
28
# Python. If the interval is set, it represents the period before
29
# the until timestamp or the period after the from timestamp,
30
# depending on which of these is set. From this information,
31
# generate the SQL representation of the from timestamp and the
34
from_sql = ("CAST(%s AS timestamp without time zone)"
35
% sqlvalues(options.from_ts))
36
elif options.interval and options.until_ts:
38
"CAST(%s AS timestamp without time zone) - CAST(%s AS interval)"
39
% sqlvalues(options.until_ts, options.interval))
40
elif options.interval:
42
"(CURRENT_TIMESTAMP AT TIME ZONE 'UTC') - CAST(%s AS interval)"
43
% sqlvalues(options.interval))
45
from_sql = "CAST('1970-01-01' AS timestamp without time zone)"
49
"CAST(%s AS timestamp without time zone)"
50
% sqlvalues(options.until_ts))
51
elif options.interval and options.from_ts:
53
"CAST(%s AS timestamp without time zone) + CAST(%s AS interval)"
54
% sqlvalues(options.from_ts, options.interval))
56
until_sql = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'"
58
clause = "date_created BETWEEN (%s) AND (%s)" % (from_sql, until_sql)
22
63
def get_table_stats(cur, options):
23
tablestats_query = """
64
params = {'where': get_where_clause(options)}
65
tablestats_query = dedent("""\
25
67
Earliest.date_created AS date_start,
26
68
Latest.date_created AS date_end,
47
89
Earliest.date_created = (
48
90
SELECT min(date_created) FROM DatabaseTableStats
49
WHERE date_created >= CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
50
- CAST(%s AS interval))
51
92
AND Latest.date_created = (
52
SELECT max(date_created) FROM DatabaseTableStats)
93
SELECT max(date_created) FROM DatabaseTableStats
53
95
AND Earliest.schemaname = Latest.schemaname
54
96
AND Earliest.relname = Latest.relname
55
""" % sqlvalues(options.since_interval)
57
98
cur.execute(tablestats_query)
59
100
# description[0] is the column name, per PEP-0249
75
116
# This query calculates the averate cpu utilization from the
76
117
# samples. It assumes samples are taken at regular intervals over
119
# Note that we have to use SUM()/COUNT() instead of AVG() as
120
# database users not connected when the sample was taken are not
121
# recorded - we want the average utilization over the time period,
122
# not the subset of the time period the user was actually connected.
123
params = {'where': get_where_clause(options)}
80
126
CAST(SUM(cpu) AS float) / (
81
127
SELECT COUNT(DISTINCT date_created) FROM DatabaseCpuStats
83
date_created >= (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')
84
- CAST (%s AS interval))
85
) AS avg_cpu, username
129
)) AS avg_cpu, username
86
130
FROM DatabaseCpuStats
87
WHERE date_created >= (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
88
- CAST(%s AS interval))
90
""" % sqlvalues(options.since_interval, options.since_interval)
92
134
cur.execute(query)
94
135
return set(cur.fetchall())
98
139
parser = LPOptionParser()
99
140
db_options(parser)
100
141
parser.add_option(
101
"-i", "--interval", dest="since_interval", type=str,
102
default="100 years", metavar="INTERVAL",
142
"-f", "--from", dest="from_ts", type=datetime,
143
default=None, metavar="TIMESTAMP",
144
help="Use statistics collected since TIMESTAMP.")
146
"-u", "--until", dest="until_ts", type=datetime,
147
default=None, metavar="TIMESTAMP",
148
help="Use statistics collected up until TIMESTAMP.")
150
"-i", "--interval", dest="interval", type=str,
151
default=None, metavar="INTERVAL",
104
153
"Use statistics collected over the last INTERVAL period. "
105
154
"INTERVAL is a string parsable by PostgreSQL "
111
160
parser.set_defaults(dbuser="database_stats_report")
112
161
options, args = parser.parse_args()
163
if options.from_ts and options.until_ts and options.interval:
165
"Only two of --from, --until and --interval may be specified.")
114
167
con = connect(options.dbuser)
115
168
cur = con.cursor()
117
tables = get_table_stats(cur, options)
118
arbitrary_table = list(tables)[0]
170
tables = list(get_table_stats(cur, options))
172
parser.error("No statistics available in that time range.")
173
arbitrary_table = tables[0]
119
174
interval = arbitrary_table.date_end - arbitrary_table.date_start
120
175
per_second = float(interval.days * 24 * 60 * 60 + interval.seconds)