-
Notifications
You must be signed in to change notification settings - Fork 327
/
whisper-auto-resize.py
executable file
·246 lines (203 loc) · 8.74 KB
/
whisper-auto-resize.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#!/usr/bin/env python
import sys
import os
import fnmatch
import shlex
from subprocess import call
from optparse import OptionParser
from distutils.spawn import find_executable
from os.path import basename
from six.moves import input
# On Debian systems whisper-resize.py is available as whisper-resize
whisperResizeExecutable = find_executable("whisper-resize.py")
if whisperResizeExecutable is None:
whisperResizeExecutable = find_executable("whisper-resize")
if whisperResizeExecutable is None:
# Probably will fail later, set it nevertheless
whisperResizeExecutable = "whisper-resize.py"
option_parser = OptionParser(
usage='''%prog storagePath configPath
storagePath the Path to the directory containing whisper files (CAN NOT BE A
SUBDIR, use --subdir for that)
configPath the path to your carbon config files
''', version="%prog 0.1")
option_parser.add_option(
'--doit', default=False, action='store_true',
help="This is not a drill, lets do it")
option_parser.add_option(
'-q', '--quiet', default=False, action='store_true',
help="Display extra debugging info")
option_parser.add_option(
'--subdir', default=None,
type='string', help="only process a subdir of whisper files")
option_parser.add_option(
'--carbonlib', default=None,
type='string', help="folder where the carbon lib files are if its not in your path already")
option_parser.add_option(
'--whisperlib', default=None,
type='string', help="folder where the whisper lib files are if its not in your path already")
option_parser.add_option(
'--confirm', default=False, action='store_true',
help="ask for comfirmation prior to resizing a whisper file")
option_parser.add_option(
'-x', '--extra_args', default='', type='string',
help="pass any additional arguments to the %s script" %
basename(whisperResizeExecutable))
(options, args) = option_parser.parse_args()
if len(args) < 2:
option_parser.print_help()
sys.exit(1)
storagePath = args[0]
configPath = args[1]
# check to see if we are processing a subfolder
# we need to have a separate config option for this since
# otherwise the metric test thinks the metric is at the root
# of the storage path and can match schemas incorrectly
if options.subdir is None:
processPath = args[0]
else:
processPath = options.subdir
# Injecting the Whisper Lib Path if needed
if options.whisperlib is not None:
sys.path.insert(0, options.whisperlib)
try:
import whisper
except ImportError:
raise SystemExit('[ERROR] Can\'t find the whisper module, try using '
'--whisperlib to explicitly include the path')
# Injecting the Carbon Lib Path if needed
if options.carbonlib is not None:
sys.path.insert(0, options.carbonlib)
try:
from carbon.conf import settings
except ImportError:
raise SystemExit('[ERROR] Can\'t find the carbon module, try using '
'--carbonlib to explicitly include the path')
# carbon.conf not seeing the config files so give it a nudge
settings.CONF_DIR = configPath
settings.LOCAL_DATA_DIR = storagePath
# import these once we have the settings figured out
from carbon.storage import loadStorageSchemas, loadAggregationSchemas
# Load the Defined Schemas from our config files
schemas = loadStorageSchemas()
agg_schemas = loadAggregationSchemas()
# check to see if a metric needs to be resized based on the current config
def processMetric(fullPath, schemas, agg_schemas):
"""
method to process a given metric, and resize it if necessary
Parameters:
fullPath - full path to the metric whisper file
schemas - carbon storage schemas loaded from config
agg_schemas - carbon storage aggregation schemas load from confg
"""
schema_config_args = ''
schema_file_args = ''
rebuild = False
messages = ''
# get archive info from whisper file
info = whisper.info(fullPath)
# get graphite metric name from fullPath
metric = getMetricFromPath(fullPath)
# loop the carbon-storage schemas
for schema in schemas:
if schema.matches(metric):
# returns secondsPerPoint and points for this schema in tuple format
archive_config = [archive.getTuple() for archive in schema.archives]
break
# loop through the carbon-aggregation schemas
for agg_schema in agg_schemas:
if agg_schema.matches(metric):
xFilesFactor, aggregationMethod = agg_schema.archives
break
if xFilesFactor is None:
xFilesFactor = 0.5
if aggregationMethod is None:
aggregationMethod = 'average'
# loop through the bucket tuples and convert to string format for resizing
for retention in archive_config:
current_schema = '%s:%s ' % (retention[0], retention[1])
schema_config_args += current_schema
# loop through the current files bucket sizes and convert to string format
# to compare for resizing
for fileRetention in info['archives']:
current_schema = '%s:%s ' % (fileRetention['secondsPerPoint'], fileRetention['points'])
schema_file_args += current_schema
# check to see if the current and configured schemas are the same or rebuild
if (schema_config_args != schema_file_args):
rebuild = True
messages += 'updating Retentions from: %s to: %s \n' % \
(schema_file_args, schema_config_args)
# only care about the first two decimals in the comparison since there is
# floaty stuff going on.
info_xFilesFactor = "{0:.2f}".format(info['xFilesFactor'])
str_xFilesFactor = "{0:.2f}".format(xFilesFactor)
# check to see if the current and configured xFilesFactor are the same
if (str_xFilesFactor != info_xFilesFactor):
rebuild = True
messages += '%s xFilesFactor differs real: %s should be: %s \n' % \
(metric, info_xFilesFactor, str_xFilesFactor)
# check to see if the current and configured aggregationMethods are the same
if (aggregationMethod != info['aggregationMethod']):
rebuild = True
messages += '%s aggregation schema differs real: %s should be: %s \n' % \
(metric, info['aggregationMethod'], aggregationMethod)
# if we need to rebuild, lets do it.
if rebuild is True:
cmd = [whisperResizeExecutable, fullPath]
for x in shlex.split(options.extra_args):
cmd.append(x)
cmd.append('--xFilesFactor=' + str(xFilesFactor))
cmd.append('--aggregationMethod=' + str(aggregationMethod))
for x in shlex.split(schema_config_args):
cmd.append(x)
if options.quiet is not True or options.confirm is True:
print(messages)
print(cmd)
if options.confirm is True:
options.doit = confirm("Would you like to run this command? [y/n]: ")
if options.doit is False:
print("Skipping command \n")
if options.doit is True:
exitcode = call(cmd)
# if the command failed lets bail so we can take a look before proceeding
if (exitcode > 0):
print('Error running: %s' % (cmd))
sys.exit(1)
def getMetricFromPath(filePath):
"""
this method takes the full file path of a whisper file an converts it
to a gaphite metric name
Parameters:
filePath - full file path to a whisper file
Returns a string representing the metric name
"""
# sanitize directory since we may get a trailing slash or not, and if we
# don't it creates a leading '.'
data_dir = os.path.normpath(settings.LOCAL_DATA_DIR) + os.sep
# pull the data dir off and convert to the graphite metric name
metric_name = filePath.replace(data_dir, '')
metric_name = metric_name.replace('.wsp', '')
metric_name = metric_name.replace('/', '.')
return metric_name
def confirm(question, error_response='Valid options : yes or no'):
"""
ask the user if they would like to perform the action
Parameters:
question - the question you would like to ask the user to confirm.
error_response - the message to display if an invalid option is given.
"""
while True:
answer = input(question).lower()
if answer in ('y', 'yes'):
return True
if answer in ('n', 'no'):
return False
print(error_response)
if os.path.isfile(processPath) and processPath.endswith('.wsp'):
processMetric(processPath, schemas, agg_schemas)
else:
for root, _, files in os.walk(processPath):
# we only want to deal with non-hidden whisper files
for f in fnmatch.filter(files, '*.wsp'):
fullpath = os.path.join(root, f)
processMetric(fullpath, schemas, agg_schemas)