heat/bin/heat-watch

282 lines
8.6 KiB
Python
Executable File

#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
This is the administration program for heat-api-cloudwatch.
It is simply a command-line interface for adding, modifying, and retrieving
information about the cloudwatch alarms and metrics belonging to a user.
It is a convenience application that talks to the heat Cloudwatch API server.
"""
import gettext
import optparse
import os
import os.path
import sys
import time
import json
import logging
from urlparse import urlparse
# If ../heat/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'heat', '__init__.py')):
sys.path.insert(0, possible_topdir)
scriptname = os.path.basename(sys.argv[0])
gettext.install('heat', unicode=1)
from heat import boto_client_cloudwatch as heat_client
from heat import version
from heat.common import config
from heat.common import exception
from heat import utils
DEFAULT_PORT=8003
@utils.catch_error('alarm-describe')
def alarm_describe(options, arguments):
'''
Describe detail for specified alarm, or all alarms
if no AlarmName is specified
'''
parameters={}
try:
parameters['AlarmName'] = arguments.pop(0)
except IndexError:
logging.info("No AlarmName passed, getting results for ALL alarms")
c = heat_client.get_client(options.port)
result = c.describe_alarm(**parameters)
print c.format_metric_alarm(result)
@utils.catch_error('alarm-set-state')
def alarm_set_state(options, arguments):
'''
Temporarily set state for specified alarm
'''
usage = ('''Usage:
%s alarm-set-state AlarmName StateValue [StateReason]''' %
(scriptname))
parameters={}
try:
parameters['AlarmName'] = arguments.pop(0)
parameters['StateValue'] = arguments.pop(0)
except IndexError:
logging.error("Must specify AlarmName and StateValue")
print usage
print "StateValue must be one of %s, %s or %s" % (
heat_client.BotoCWClient.ALARM_STATES)
return utils.FAILURE
try:
parameters['StateReason'] = arguments.pop(0)
except IndexError:
parameters['StateReason'] = ""
# We don't handle attaching data to state via this cli tool (yet)
parameters['StateReasonData'] = None
c = heat_client.get_client(options.port)
result = c.set_alarm_state(**parameters)
print result
@utils.catch_error('metric-list')
def metric_list(options, arguments):
'''
List all metric data for a given metric (or all metrics if none specified)
'''
parameters={}
try:
parameters['MetricName'] = arguments.pop(0)
except IndexError:
logging.info("No MetricName passed, getting results for ALL alarms")
c = heat_client.get_client(options.port)
result = c.list_metrics(**parameters)
print c.format_metric(result)
@utils.catch_error('metric-put-data')
def metric_put_data(options, arguments):
'''
Create a datapoint for a specified metric
'''
usage = ('''Usage:
%s metric-put-data AlarmName Namespace MetricName Units MetricValue
e.g
%s metric-put-data HttpFailureAlarm system/linux ServiceFailure Count 1
''' % (scriptname, scriptname))
# NOTE : we currently only support metric datapoints associated with a
# specific AlarmName, due to the current engine/db cloudwatch
# implementation, we should probably revisit this so we can support
# more generic metric data collection
parameters={}
try:
parameters['AlarmName'] = arguments.pop(0)
parameters['Namespace'] = arguments.pop(0)
parameters['MetricName'] = arguments.pop(0)
parameters['MetricUnit'] = arguments.pop(0)
parameters['MetricValue'] = arguments.pop(0)
except IndexError:
logging.error("Please specify the alarm, metric, unit and value")
print usage
return utils.FAILURE
c = heat_client.get_client(options.port)
result = c.put_metric_data(**parameters)
print result
def create_options(parser):
"""
Sets up the CLI and config-file options that may be
parsed and program commands.
:param parser: The option parser
"""
parser.add_option('-v', '--verbose', default=False, action="store_true",
help="Print more verbose output")
parser.add_option('-d', '--debug', default=False, action="store_true",
help="Print more verbose output")
parser.add_option('-p', '--port', dest="port", type=int,
default=DEFAULT_PORT,
help="Port the heat API host listens on. "
"Default: %default")
def parse_options(parser, cli_args):
"""
Returns the parsed CLI options, command to run and its arguments, merged
with any same-named options found in a configuration file
:param parser: The option parser
"""
if not cli_args:
cli_args.append('-h') # Show options in usage output...
(options, args) = parser.parse_args(cli_args)
# HACK(sirp): Make the parser available to the print_help method
# print_help is a command, so it only accepts (options, args); we could
# one-off have it take (parser, options, args), however, for now, I think
# this little hack will suffice
options.__parser = parser
if not args:
parser.print_usage()
sys.exit(0)
command_name = args.pop(0)
command = lookup_command(parser, command_name)
if options.debug:
logging.basicConfig(format='%(levelname)s:%(message)s',
level=logging.DEBUG)
logging.debug("Debug level logging enabled")
elif options.verbose:
logging.basicConfig(format='%(levelname)s:%(message)s',
level=logging.INFO)
else:
logging.basicConfig(format='%(levelname)s:%(message)s',
level=logging.WARNING)
return (options, command, args)
def print_help(options, args):
"""
Print help specific to a command
"""
parser = options.__parser
if not args:
parser.print_usage()
subst = {'prog': os.path.basename(sys.argv[0])}
docs = [lookup_command(parser, cmd).__doc__ % subst for cmd in args]
print '\n\n'.join(docs)
def lookup_command(parser, command_name):
base_commands = {'help': print_help}
watch_commands = {
'describe': alarm_describe,
'set-state': alarm_set_state,
'metric-list': metric_list,
'metric-put-data': metric_put_data}
commands = {}
for command_set in (base_commands, watch_commands):
commands.update(command_set)
try:
command = commands[command_name]
except KeyError:
parser.print_usage()
sys.exit("Unknown command: %s" % command_name)
return command
def main():
'''
'''
usage = """
%prog <command> [options] [args]
Commands:
help <command> Output help for one of the commands below
describe Describe a specified alarm (or all alarms)
set-state Temporarily set the state of an alarm
metric-list List data-points for specified metric
metric-put-data Publish data-point for specified metric
"""
oparser = optparse.OptionParser(version='%%prog %s'
% version.version_string(),
usage=usage.strip())
create_options(oparser)
(opts, cmd, args) = parse_options(oparser, sys.argv[1:])
try:
start_time = time.time()
result = cmd(opts, args)
end_time = time.time()
logging.debug("Completed in %-0.4f sec." % (end_time - start_time))
sys.exit(result)
except (RuntimeError,
NotImplementedError,
exception.ClientConfigurationError), ex:
oparser.print_usage()
logging.error("ERROR: %s" % ex)
sys.exit(1)
if __name__ == '__main__':
main()