OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 Google Inc. All Rights Reserved. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 """metric values provides funcs using to aggregate `MetricValue`. |
| 16 |
| 17 :func:`merge` merges two `MetricValue` instances. |
| 18 :func:`update_hash` adds a `MetricValue` to a secure hash |
| 19 :func:`sign` generates a signature for a `MetricValue` using a secure hash |
| 20 |
| 21 """ |
| 22 |
| 23 from __future__ import absolute_import |
| 24 |
| 25 import hashlib |
| 26 import logging |
| 27 |
| 28 from apitools.base.py import encoding |
| 29 |
| 30 from . import distribution, money, signing, timestamp, MetricKind |
| 31 from google.api.gen.servicecontrol_v1_messages import MetricValue |
| 32 |
| 33 |
| 34 logger = logging.getLogger(__name__) |
| 35 |
| 36 |
| 37 def create(labels=None, **kw): |
| 38 """Constructs a new metric value. |
| 39 |
| 40 This acts as an alternate to MetricValue constructor which |
| 41 simplifies specification of labels. Rather than having to create |
| 42 a MetricValue.Labels instance, all that's necessary to specify the |
| 43 required string. |
| 44 |
| 45 Args: |
| 46 labels (dict([string, [string]]): |
| 47 **kw: any other valid keyword args valid in the MetricValue constructor |
| 48 |
| 49 Returns |
| 50 :class:`MetricValue`: the created instance |
| 51 |
| 52 """ |
| 53 if labels is not None: |
| 54 kw['labels'] = encoding.PyValueToMessage(MetricValue.LabelsValue, |
| 55 labels) |
| 56 return MetricValue(**kw) |
| 57 |
| 58 |
| 59 def merge(metric_kind, prior, latest): |
| 60 """Merges `prior` and `latest` |
| 61 |
| 62 Args: |
| 63 metric_kind (:class:`MetricKind`): indicates the kind of metrics |
| 64 being merged |
| 65 prior (:class:`MetricValue`): an prior instance of the metric |
| 66 latest (:class:`MetricValue`: the latest instance of the metric |
| 67 """ |
| 68 prior_type, _ = _detect_value(prior) |
| 69 latest_type, _ = _detect_value(latest) |
| 70 if prior_type != latest_type: |
| 71 logger.warn('Metric values are not compatible: %s, %s', |
| 72 prior, latest) |
| 73 raise ValueError('Incompatible delta metric values') |
| 74 if prior_type is None: |
| 75 logger.warn('Bad metric values, types not known for : %s, %s', |
| 76 prior, latest) |
| 77 raise ValueError('Unsupported delta metric types') |
| 78 |
| 79 if metric_kind == MetricKind.DELTA: |
| 80 return _merge_delta_metric(prior, latest) |
| 81 else: |
| 82 return _merge_cumulative_or_gauge_metrics(prior, latest) |
| 83 |
| 84 |
| 85 def update_hash(a_hash, mv): |
| 86 """Adds ``mv`` to ``a_hash`` |
| 87 |
| 88 Args: |
| 89 a_hash (`Hash`): the secure hash, e.g created by hashlib.md5 |
| 90 mv (:class:`MetricValue`): the instance to add to the hash |
| 91 |
| 92 """ |
| 93 if mv.labels: |
| 94 signing.add_dict_to_hash(a_hash, encoding.MessageToPyValue(mv.labels)) |
| 95 money_value = mv.get_assigned_value('moneyValue') |
| 96 if money_value is not None: |
| 97 a_hash.update('\x00') |
| 98 a_hash.update(money_value.currencyCode) |
| 99 |
| 100 |
| 101 def sign(mv): |
| 102 """Obtains a signature for a `MetricValue` |
| 103 |
| 104 Args: |
| 105 mv (:class:`google.api.gen.servicecontrol_v1_messages.MetricValue`): a |
| 106 MetricValue that's part of an operation |
| 107 |
| 108 Returns: |
| 109 string: a unique signature for that operation |
| 110 """ |
| 111 md5 = hashlib.md5() |
| 112 update_hash(md5, mv) |
| 113 return md5.digest() |
| 114 |
| 115 |
| 116 def _merge_cumulative_or_gauge_metrics(prior, latest): |
| 117 if timestamp.compare(prior.endTime, latest.endTime) == -1: |
| 118 return latest |
| 119 else: |
| 120 return prior |
| 121 |
| 122 |
| 123 def _merge_delta_metric(prior, latest): |
| 124 prior_type, prior_value = _detect_value(prior) |
| 125 latest_type, latest_value = _detect_value(latest) |
| 126 _merge_delta_timestamps(prior, latest) |
| 127 updated_value = _combine_delta_values(prior_type, prior_value, latest_value) |
| 128 setattr(latest, latest_type, updated_value) |
| 129 return latest |
| 130 |
| 131 |
| 132 # This is derived from the oneof choices for the MetricValue message's value |
| 133 # field in google/api/servicecontrol/v1/metric_value.proto, and should be kept |
| 134 # in sync with that |
| 135 _METRIC_VALUE_ONEOF_FIELDS = ( |
| 136 'boolValue', 'distributionValue', 'doubleValue', 'int64Value', |
| 137 'moneyValue', 'stringValue') |
| 138 |
| 139 |
| 140 def _detect_value(metric_value): |
| 141 for f in _METRIC_VALUE_ONEOF_FIELDS: |
| 142 value = metric_value.get_assigned_value(f) |
| 143 if value is not None: |
| 144 return f, value |
| 145 return None, None |
| 146 |
| 147 |
| 148 def _merge_delta_timestamps(prior, latest): |
| 149 # Update the start time and end time in the latest metric value |
| 150 if (prior.startTime and |
| 151 (latest.startTime is None or |
| 152 timestamp.compare(prior.startTime, latest.startTime) == -1)): |
| 153 latest.startTime = prior.startTime |
| 154 |
| 155 if (prior.endTime and |
| 156 (latest.endTime is None or timestamp.compare( |
| 157 latest.endTime, prior.endTime) == -1)): |
| 158 latest.endTime = prior.endTime |
| 159 |
| 160 return latest |
| 161 |
| 162 |
| 163 def _combine_delta_values(value_type, prior, latest): |
| 164 if value_type in ('int64Value', 'doubleValue'): |
| 165 return prior + latest |
| 166 elif value_type == 'moneyValue': |
| 167 return money.add(prior, latest, allow_overflow=True) |
| 168 elif value_type == 'distributionValue': |
| 169 distribution.merge(prior, latest) |
| 170 return latest |
| 171 else: |
| 172 logger.error('Unmergeable metric type %s', value_type) |
| 173 raise ValueError('Could not merge unmergeable metric type') |
OLD | NEW |