Skip to content
Permalink
Browse files
Fix work of udp publisher
 * Signature is appended also to udp messages
 * All methods for convert from sample to message are moved to publisher/utils.py
 * In configs metering_secret is moved from group publisher_rpc to publisher
 * Changed tests for udp publisher

Fixes: bug #1259171

Change-Id: Ic7727c69d4be6d17ff00f21e323341efd57122a7
  • Loading branch information
Ilya Tyaptin committed Jan 9, 2014
1 parent e564c1e commit cdab1401dc9cc6fdf7d079c1d139ad915a7d47da
Show file tree
Hide file tree
Showing 28 changed files with 361 additions and 327 deletions.
@@ -77,10 +77,6 @@ def start_udp(self):
LOG.warn(_("UDP: Cannot decode data sent by %s"), str(source))
else:
try:
sample['counter_name'] = sample['name']
sample['counter_volume'] = sample['volume']
sample['counter_unit'] = sample['unit']
sample['counter_type'] = sample['type']
LOG.debug(_("UDP: Storing %s"), str(sample))
self.dispatcher_manager.map_method('record_metering_data',
sample)
@@ -20,7 +20,7 @@
from ceilometer.openstack.common.gettextutils import _ # noqa
from ceilometer.openstack.common import log
from ceilometer.openstack.common import timeutils
from ceilometer.publisher import rpc as publisher_rpc
from ceilometer.publisher import utils as publisher_utils
from ceilometer import storage

LOG = log.getLogger(__name__)
@@ -54,9 +54,9 @@ def record_metering_data(self, data):
'resource_id': meter['resource_id'],
'timestamp': meter.get('timestamp', 'NO TIMESTAMP'),
'counter_volume': meter['counter_volume']}))
if publisher_rpc.verify_signature(
if publisher_utils.verify_signature(
meter,
self.conf.publisher_rpc.metering_secret):
self.conf.publisher.metering_secret):
try:
# Convert the timestamp to a datetime instance.
# Storage engines are responsible for converting
@@ -18,8 +18,7 @@
"""Publish a sample using the preferred RPC mechanism.
"""

import hashlib
import hmac

import itertools
import operator
import urlparse
@@ -30,7 +29,7 @@
from ceilometer.openstack.common import log
from ceilometer.openstack.common import rpc
from ceilometer import publisher
from ceilometer import utils
from ceilometer.publisher import utils


LOG = log.getLogger(__name__)
@@ -41,12 +40,6 @@
help='the topic ceilometer uses for metering messages',
deprecated_group="DEFAULT",
),
cfg.StrOpt('metering_secret',
secret=True,
default='change this or be hacked',
help='Secret value for signing metering messages',
deprecated_group="DEFAULT",
),
]


@@ -91,51 +84,6 @@ def override_backend_retry_config(value):
cfg.CONF.set_override('rabbit_max_retries', value)


def compute_signature(message, secret):
"""Return the signature for a message dictionary.
"""
digest_maker = hmac.new(secret, '', hashlib.sha256)
for name, value in utils.recursive_keypairs(message):
if name == 'message_signature':
# Skip any existing signature value, which would not have
# been part of the original message.
continue
digest_maker.update(name)
digest_maker.update(unicode(value).encode('utf-8'))
return digest_maker.hexdigest()


def verify_signature(message, secret):
"""Check the signature in the message against the value computed
from the rest of the contents.
"""
old_sig = message.get('message_signature')
new_sig = compute_signature(message, secret)
return new_sig == old_sig


def meter_message_from_counter(sample, secret):
"""Make a metering message ready to be published or stored.
Returns a dictionary containing a metering message
for a notification message and a Sample instance.
"""
msg = {'source': sample.source,
'counter_name': sample.name,
'counter_type': sample.type,
'counter_unit': sample.unit,
'counter_volume': sample.volume,
'user_id': sample.user_id,
'project_id': sample.project_id,
'resource_id': sample.resource_id,
'timestamp': sample.timestamp,
'resource_metadata': sample.resource_metadata,
'message_id': sample.id,
}
msg['message_signature'] = compute_signature(msg, secret)
return msg


class RPCPublisher(publisher.PublisherBase):

def __init__(self, parsed_url):
@@ -175,9 +123,9 @@ def publish_samples(self, context, samples):
"""

meters = [
meter_message_from_counter(
utils.meter_message_from_counter(
sample,
cfg.CONF.publisher_rpc.metering_secret)
cfg.CONF.publisher.metering_secret)
for sample in samples
]

@@ -2,7 +2,8 @@
#
# Copyright © 2013 eNovance
#
# Author: Julien Danjou <julien@danjou.info>
# Author: Julien Danjou <julien@danjou.info>,
# Tyaptin Ilya <ityaptin@mirantis.com>
#
# 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
@@ -27,6 +28,7 @@
from ceilometer.openstack.common import log
from ceilometer.openstack.common import network_utils
from ceilometer import publisher
from ceilometer.publisher import utils

cfg.CONF.import_opt('udp_port', 'ceilometer.collector',
group='collector')
@@ -35,7 +37,6 @@


class UDPPublisher(publisher.PublisherBase):

def __init__(self, parsed_url):
self.host, self.port = network_utils.parse_host_port(
parsed_url.netloc,
@@ -51,7 +52,9 @@ def publish_samples(self, context, samples):
"""

for sample in samples:
msg = sample.as_dict()
msg = utils.meter_message_from_counter(
sample,
cfg.CONF.publisher.metering_secret)
host = self.host
port = self.port
LOG.debug(_("Publishing sample %(msg)s over UDP to "
@@ -0,0 +1,93 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2012 New Dream Network, LLC (DreamHost)
#
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
# Tyaptin Ilya <ityaptin@mirantis.com>
#
# 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.
"""Utils for publishers
"""

import hashlib
import hmac

from oslo.config import cfg

from ceilometer import utils

METER_PUBLISH_OPTS = [
cfg.StrOpt('metering_secret',
secret=True,
default='change this or be hacked',
help='Secret value for signing metering messages',
deprecated_opts=[cfg.DeprecatedOpt("metering_secret",
"DEFAULT"),
cfg.DeprecatedOpt("metering_secret",
"publisher_rpc")]
),
]


def register_opts(config):
"""Register the options for publishing metering messages.
"""
config.register_opts(METER_PUBLISH_OPTS, group="publisher")


register_opts(cfg.CONF)


def compute_signature(message, secret):
"""Return the signature for a message dictionary.
"""
digest_maker = hmac.new(secret, '', hashlib.sha256)
for name, value in utils.recursive_keypairs(message):
if name == 'message_signature':
# Skip any existing signature value, which would not have
# been part of the original message.
continue
digest_maker.update(name)
digest_maker.update(unicode(value).encode('utf-8'))
return digest_maker.hexdigest()


def verify_signature(message, secret):
"""Check the signature in the message against the value computed
from the rest of the contents.
"""
old_sig = message.get('message_signature')
new_sig = compute_signature(message, secret)
return new_sig == old_sig


def meter_message_from_counter(sample, secret):
"""Make a metering message ready to be published or stored.
Returns a dictionary containing a metering message
for a notification message and a Sample instance.
"""
msg = {'source': sample.source,
'counter_name': sample.name,
'counter_type': sample.type,
'counter_unit': sample.unit,
'counter_volume': sample.volume,
'user_id': sample.user_id,
'project_id': sample.project_id,
'resource_id': sample.resource_id,
'timestamp': sample.timestamp,
'resource_metadata': sample.resource_metadata,
'message_id': sample.id,
}
msg['message_signature'] = compute_signature(msg, secret)
return msg
@@ -22,7 +22,7 @@
import datetime
import testscenarios

from ceilometer.publisher import rpc
from ceilometer.publisher import utils
from ceilometer import sample

from ceilometer.tests import api as tests_api
@@ -77,9 +77,9 @@ def setUp(self):
source='source1',
),
]:
msg = rpc.meter_message_from_counter(
msg = utils.meter_message_from_counter(
cnt,
self.CONF.publisher_rpc.metering_secret)
self.CONF.publisher.metering_secret)
self.conn.record_metering_data(msg)

def test_empty_project(self):
@@ -23,7 +23,7 @@
import logging
import testscenarios

from ceilometer.publisher import rpc
from ceilometer.publisher import utils
from ceilometer import sample

from ceilometer.tests import api as tests_api
@@ -108,9 +108,9 @@ def setUp(self):
resource_metadata={'display_name': 'test-server',
'tag': 'four.sample'},
source='test_list_resources')]:
msg = rpc.meter_message_from_counter(
msg = utils.meter_message_from_counter(
cnt,
self.CONF.publisher_rpc.metering_secret)
self.CONF.publisher.metering_secret)
self.conn.record_metering_data(msg)

def test_list_meters(self):
@@ -23,7 +23,7 @@
import logging
import testscenarios

from ceilometer.publisher import rpc
from ceilometer.publisher import utils
from ceilometer import sample

from ceilometer.tests import api as tests_api
@@ -60,9 +60,9 @@ def setUp(self):
'tag': 'self.sample'},
source='test_list_projects',
)
msg = rpc.meter_message_from_counter(
msg = utils.meter_message_from_counter(
sample1,
self.CONF.publisher_rpc.metering_secret,
self.CONF.publisher.metering_secret,
)
self.conn.record_metering_data(msg)

@@ -79,9 +79,9 @@ def setUp(self):
'tag': 'self.sample2'},
source='test_list_users',
)
msg2 = rpc.meter_message_from_counter(
msg2 = utils.meter_message_from_counter(
sample2,
self.CONF.publisher_rpc.metering_secret,
self.CONF.publisher.metering_secret,
)
self.conn.record_metering_data(msg2)

@@ -23,7 +23,7 @@
import logging
import testscenarios

from ceilometer.publisher import rpc
from ceilometer.publisher import utils
from ceilometer import sample

from ceilometer.tests import api as tests_api
@@ -101,9 +101,9 @@ def setUp(self):
'tag': 'self.sample4'},
source='test_list_resources',
)]:
msg = rpc.meter_message_from_counter(
msg = utils.meter_message_from_counter(
cnt,
self.CONF.publisher_rpc.metering_secret)
self.CONF.publisher.metering_secret)
self.conn.record_metering_data(msg)


@@ -23,7 +23,7 @@
import logging
import testscenarios

from ceilometer.publisher import rpc
from ceilometer.publisher import utils
from ceilometer import sample

from ceilometer.tests import api as tests_api
@@ -62,9 +62,9 @@ def setUp(self):
},
source='test_list_users',
)
msg = rpc.meter_message_from_counter(
msg = utils.meter_message_from_counter(
sample1,
self.CONF.publisher_rpc.metering_secret,
self.CONF.publisher.metering_secret,
)
self.conn.record_metering_data(msg)

@@ -82,9 +82,9 @@ def setUp(self):
},
source='not-test',
)
msg2 = rpc.meter_message_from_counter(
msg2 = utils.meter_message_from_counter(
sample2,
self.CONF.publisher_rpc.metering_secret,
self.CONF.publisher.metering_secret,
)
self.conn.record_metering_data(msg2)

0 comments on commit cdab140

Please sign in to comment.