# 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.
"""
Client implementation based on the boto AWS client library
"""
from heat.openstack.common import log as logging
logger = logging.getLogger(__name__)
from boto.ec2.cloudwatch import CloudWatchConnection
[docs]class BotoCWClient(CloudWatchConnection):
'''
Wrapper class for boto CloudWatchConnection class
'''
# TODO : These should probably go in the CW API and be imported
DEFAULT_NAMESPACE = "heat/unknown"
METRIC_UNITS = ("Seconds", "Microseconds", "Milliseconds", "Bytes",
"Kilobytes", "Megabytes", "Gigabytes", "Terabytes",
"Bits", "Kilobits", "Megabits", "Gigabits", "Terabits",
"Percent", "Count", "Bytes/Second", "Kilobytes/Second",
"Megabytes/Second", "Gigabytes/Second", "Terabytes/Second",
"Bits/Second", "Kilobits/Second", "Megabits/Second",
"Gigabits/Second", "Terabits/Second", "Count/Second", None)
METRIC_COMPARISONS = (">=", ">", "<", "<=")
ALARM_STATES = ("OK", "ALARM", "INSUFFICIENT_DATA")
METRIC_STATISTICS = ("Average", "Sum", "SampleCount", "Maximum", "Minimum")
# Note, several of these boto calls take a list of alarm names, so
# we could easily handle multiple alarms per-action, but in the
# interests of keeping the client simple, we just handle one 'AlarmName'
[docs] def describe_alarm(self, **kwargs):
# If no AlarmName specified, we pass None, which returns
# results for ALL alarms
try:
name = kwargs['AlarmName']
except KeyError:
name = None
return super(BotoCWClient, self).describe_alarms(
alarm_names=[name])
[docs] def list_metrics(self, **kwargs):
# list_metrics returns non-null index in next_token if there
# are more than 500 metric results, in which case we have to
# re-read with the token to get the next batch of results
#
# Also note that we can do more advanced filtering by dimension
# and/or namespace, but for simplicity we only filter by
# MetricName for the time being
try:
name = kwargs['MetricName']
except KeyError:
name = None
results = []
token = None
while True:
results.append(super(BotoCWClient, self).list_metrics(
next_token=token,
dimensions=None,
metric_name=name,
namespace=None))
if not token:
break
return results
[docs] def put_metric_data(self, **kwargs):
'''
Publish metric data points to CloudWatch
'''
try:
metric_name = kwargs['MetricName']
metric_unit = kwargs['MetricUnit']
metric_value = kwargs['MetricValue']
metric_namespace = kwargs['Namespace']
except KeyError:
logger.error("Must pass MetricName, MetricUnit, " +
"Namespace, MetricValue!")
return
try:
metric_unit = kwargs['MetricUnit']
except KeyError:
metric_unit = None
# If we're passed AlarmName, we attach it to the metric
# as a dimension
try:
metric_dims = [{'AlarmName': kwargs['AlarmName']}]
except KeyError:
metric_dims = []
if metric_unit not in self.METRIC_UNITS:
logger.error("MetricUnit not an allowed value")
logger.error("MetricUnit must be one of %s" % self.METRIC_UNITS)
return
return super(BotoCWClient, self).put_metric_data(
namespace=metric_namespace,
name=metric_name,
value=metric_value,
timestamp=None, # This means use "now" in the engine
unit=metric_unit,
dimensions=metric_dims,
statistics=None)
[docs] def set_alarm_state(self, **kwargs):
return super(BotoCWClient, self).set_alarm_state(
alarm_name=kwargs['AlarmName'],
state_reason=kwargs['StateReason'],
state_value=kwargs['StateValue'],
state_reason_data=kwargs['StateReasonData'])
[docs]def get_client(port=None, aws_access_key=None, aws_secret_key=None):
"""
Returns a new boto CloudWatch client connection to a heat server
Note : Configuration goes in /etc/boto.cfg, not via arguments
"""
# Note we pass None/None for the keys so boto reads /etc/boto.cfg
# Also note is_secure is defaulted to False as HTTPS connections
# don't seem to work atm, FIXME
cloudwatch = BotoCWClient(aws_access_key_id=aws_access_key,
aws_secret_access_key=aws_secret_key,
is_secure=False,
port=port,
path="/v1")
if cloudwatch:
logger.debug("Got CW connection object OK")
else:
logger.error("Error establishing CloudWatch connection!")
sys.exit(1)
return cloudwatch