Commit 40365b33 authored by Thomas Goirand's avatar Thomas Goirand

Merge tag '6.0.0_rc1' into debian/mitaka

ceilometer 6.0.0.0rc1 release candidate

meta:version: 6.0.0.0rc1
meta:series: mitaka
meta:release-type: release candidate
meta:announce: openstack-announce@lists.openstack.org
meta:pypi: no
meta:first: no
parents bedccc63 6efe3c3c
......@@ -10,9 +10,6 @@ approving a patch if it is in any way controversial or risky.
= IRC handles of maintainers =
cdent
dhellmann
DinaBelova
eglynn
gordc
ildikov
jd__
......
......@@ -29,9 +29,8 @@ CONF = cfg.CONF
class MultiChoicesOpt(cfg.Opt):
def __init__(self, name, choices=None, **kwargs):
super(MultiChoicesOpt, self).__init__(name,
type=DeduplicatedCfgList(),
**kwargs)
super(MultiChoicesOpt, self).__init__(
name, type=DeduplicatedCfgList(choices), **kwargs)
self.choices = choices
def _get_argparse_kwargs(self, group, **kwargs):
......@@ -45,12 +44,20 @@ class MultiChoicesOpt(cfg.Opt):
class DeduplicatedCfgList(cfg.types.List):
def __init__(self, choices=None, **kwargs):
super(DeduplicatedCfgList, self).__init__(**kwargs)
self.choices = choices or []
def __call__(self, *args, **kwargs):
result = super(DeduplicatedCfgList, self).__call__(*args, **kwargs)
if len(result) != len(set(result)):
result_set = set(result)
if len(result) != len(result_set):
LOG.warning(_LW("Duplicated values: %s found in CLI options, "
"auto de-duplidated"), result)
result = list(set(result))
"auto de-duplicated"), result)
result = list(result_set)
if self.choices and not (result_set <= set(self.choices)):
raise Exception('Valid values are %s, but found %s'
% (self.choices, result))
return result
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2012-2014 Julien Danjou
......
......@@ -48,6 +48,11 @@ class CPUPollster(pollsters.BaseComputePollster):
except virt_inspector.InstanceNotFoundException as err:
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except ceilometer.NotImplementedError:
# Selected inspector does not implement this pollster.
LOG.debug('Obtaining CPU time is not implemented for %s',
......
......@@ -24,7 +24,7 @@ import ceilometer
from ceilometer.compute import pollsters
from ceilometer.compute.pollsters import util
from ceilometer.compute.virt import inspector as virt_inspector
from ceilometer.i18n import _, _LW
from ceilometer.i18n import _
from ceilometer import sample
LOG = log.getLogger(__name__)
......@@ -160,10 +160,10 @@ class _Base(pollsters.BaseComputePollster):
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.warning(_LW('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s'),
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except ceilometer.NotImplementedError:
# Selected inspector does not implement this pollster.
LOG.debug('%(inspector)s does not provide data for '
......@@ -630,10 +630,10 @@ class _DiskInfoPollsterBase(pollsters.BaseComputePollster):
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.warning(_LW('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s'),
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except ceilometer.NotImplementedError:
# Selected inspector does not implement this pollster.
LOG.debug('%(inspector)s does not provide data for '
......
......@@ -48,10 +48,10 @@ class MemoryUsagePollster(pollsters.BaseComputePollster):
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.warning(_LW('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s'),
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except virt_inspector.NoDataException as e:
LOG.warning(_LW('Cannot inspect data of %(pollster)s for '
'%(instance_id)s, non-fatal reason: %(exc)s'),
......@@ -91,10 +91,10 @@ class MemoryResidentPollster(pollsters.BaseComputePollster):
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.warning(_LW('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s'),
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except virt_inspector.NoDataException as e:
LOG.warning(_LW('Cannot inspect data of %(pollster)s for '
'%(instance_id)s, non-fatal reason: %(exc)s'),
......
......@@ -22,7 +22,7 @@ import ceilometer
from ceilometer.compute import pollsters
from ceilometer.compute.pollsters import util
from ceilometer.compute.virt import inspector as virt_inspector
from ceilometer.i18n import _, _LW
from ceilometer.i18n import _
from ceilometer import sample
LOG = log.getLogger(__name__)
......@@ -96,10 +96,10 @@ class _Base(pollsters.BaseComputePollster):
# Instance was deleted while getting samples. Ignore it.
LOG.debug('Exception while getting samples %s', err)
except virt_inspector.InstanceShutOffException as e:
LOG.warning(_LW('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s'),
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
LOG.debug('Instance %(instance_id)s was shut off while '
'getting samples of %(pollster)s: %(exc)s',
{'instance_id': instance.id,
'pollster': self.__class__.__name__, 'exc': e})
except ceilometer.NotImplementedError:
# Selected inspector does not implement this pollster.
LOG.debug('%(inspector)s does not provide data for '
......
......@@ -110,7 +110,7 @@ class LibvirtInspector(virt_inspector.Inspector):
raise virt_inspector.InstanceNotFoundException(msg)
def inspect_cpus(self, instance):
domain = self._lookup_by_uuid(instance)
domain = self._get_domain_not_shut_off_or_raise(instance)
dom_info = domain.info()
return virt_inspector.CPUStats(number=dom_info[3], time=dom_info[4])
......
......@@ -15,6 +15,7 @@
from oslo_config import cfg
from oslo_utils import units
import six.moves.urllib.parse as urlparse
try:
import XenAPI as api
except ImportError:
......@@ -48,6 +49,18 @@ class XenapiException(virt_inspector.InspectorException):
pass
def swap_xapi_host(url, host_addr):
"""Replace the XenServer address present in 'url' with 'host_addr'."""
temp_url = urlparse.urlparse(url)
# The connection URL is served by XAPI and doesn't support having a
# path for the connection url after the port. And username/password
# will be pass separately. So the URL like "http://abc:abc@abc:433/abc"
# should not appear for XAPI case.
temp_netloc = temp_url.netloc.replace(temp_url.hostname, '%s' % host_addr)
replaced = temp_url._replace(netloc=temp_netloc)
return urlparse.urlunparse(replaced)
def get_api_session():
if not api:
raise ImportError(_('XenAPI not installed'))
......@@ -64,8 +77,18 @@ def get_api_session():
else api.Session(url))
session.login_with_password(username, password)
except api.Failure as e:
msg = _("Could not connect to XenAPI: %s") % e.details[0]
raise XenapiException(msg)
if e.details[0] == 'HOST_IS_SLAVE':
master = e.details[1]
url = swap_xapi_host(url, master)
try:
session = api.Session(url)
session.login_with_password(username, password)
except api.Failure as es:
raise XenapiException(_('Could not connect slave host: %s ') %
es.details[0])
else:
msg = _("Could not connect to XenAPI: %s") % e.details[0]
raise XenapiException(msg)
return session
......
# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
#
# 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.
from oslo_config import cfg
from oslo_middleware import cors
def set_cors_middleware_defaults():
"""Update default configuration options for oslo.middleware."""
# CORS Defaults
# TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/
cfg.set_defaults(cors.CORS_OPTS,
allow_headers=['X-Auth-Token',
'X-Identity-Status',
'X-Roles',
'X-Service-Catalog',
'X-User-Id',
'X-Tenant-Id',
'X-Openstack-Request-Id'],
expose_headers=['X-Auth-Token',
'X-Subject-Token',
'X-Service-Token',
'X-Openstack-Request-Id'],
allow_methods=['GET',
'PUT',
'POST',
'DELETE',
'PATCH']
)
#
# Copyright 2015 Hewlett Packard
#
# 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.
from oslo_config import cfg
import oslo_messaging
from oslo_utils import timeutils
from ceilometer.agent import plugin_base
from ceilometer import sample
SERVICE = 'trove'
cfg.CONF.import_opt('trove_control_exchange',
'ceilometer.notification')
class TroveMetricsNotificationBase(plugin_base.NotificationBase):
"""Base class for trove (dbaas) notifications."""
def get_targets(self, conf):
"""Return a sequence of oslo.messaging.Target
This sequence is defining the exchange and topics to be connected for
this plugin.
"""
return [oslo_messaging.Target(topic=topic,
exchange=conf.trove_control_exchange)
for topic in self.get_notification_topics(conf)]
class InstanceExists(TroveMetricsNotificationBase):
"""Handles Trove instance exists notification.
Emits a sample for a measurable audit interval.
"""
event_types = ['%s.instance.exists' % SERVICE]
def process_notification(self, message):
period_start = timeutils.normalize_time(timeutils.parse_isotime(
message['payload']['audit_period_beginning']))
period_end = timeutils.normalize_time(timeutils.parse_isotime(
message['payload']['audit_period_ending']))
period_difference = timeutils.delta_seconds(period_start, period_end)
yield sample.Sample.from_notification(
name=message['event_type'],
type=sample.TYPE_CUMULATIVE,
unit='s',
volume=period_difference,
resource_id=message['payload']['instance_id'],
user_id=message['payload']['user_id'],
project_id=message['payload']['tenant_id'],
message=message)
......@@ -32,6 +32,7 @@ class DefinitionException(Exception):
class Definition(object):
JSONPATH_RW_PARSER = parser.ExtentedJsonPathParser()
GETTERS_CACHE = {}
def __init__(self, name, cfg, plugin_manager):
self.cfg = cfg
......@@ -85,7 +86,7 @@ class Definition(object):
self.getter = fields
else:
try:
self.getter = self.JSONPATH_RW_PARSER.parse(fields).find
self.getter = self.make_getter(fields)
except Exception as e:
raise DefinitionException(
_("Parse error in JSONPath specification "
......@@ -123,6 +124,14 @@ class Definition(object):
else:
return values[0] if values else None
def make_getter(self, fields):
if fields in self.GETTERS_CACHE:
return self.GETTERS_CACHE[fields]
else:
getter = self.JSONPATH_RW_PARSER.parse(fields).find
self.GETTERS_CACHE[fields] = getter
return getter
def load_definitions(defaults, config_file, fallback_file=None):
"""Setup a definitions from yaml config file."""
......
#
# Copyright 2015 Hewlett Packard
#
# 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.
from oslo_config import cfg
import oslo_messaging
from oslo_utils import timeutils
from ceilometer.agent import plugin_base
from ceilometer import sample
OPTS = [
cfg.StrOpt('dns_control_exchange',
default='central',
help="Exchange name for DNS notifications."),
]
cfg.CONF.register_opts(OPTS)
SERVICE = 'dns'
class DnsMetricsNotificationBase(plugin_base.NotificationBase):
"""Base class for DNSaaS(Designate) notifications."""
def get_targets(self, conf):
"""Return a sequence of oslo.messaging.Target
This sequence is defining the exchange and topics to be connected for
this plugin.
"""
return [oslo_messaging.Target(topic=topic,
exchange=conf.dns_control_exchange)
for topic in self.get_notification_topics(conf)]
class DomainExists(DnsMetricsNotificationBase):
"""Handles DNS domain exists notification.
Emits a sample for a measurable audit interval.
"""
event_types = ['%s.domain.exists' % SERVICE]
def process_notification(self, message):
period_start = timeutils.normalize_time(timeutils.parse_isotime(
message['payload']['audit_period_beginning']))
period_end = timeutils.normalize_time(timeutils.parse_isotime(
message['payload']['audit_period_ending']))
period_difference = timeutils.delta_seconds(period_start, period_end)
yield sample.Sample.from_notification(
name=message['event_type'],
type=sample.TYPE_CUMULATIVE,
unit='s',
volume=period_difference,
resource_id=message['payload']['id'],
user_id=message['_context_user'],
project_id=message['payload']['tenant_id'],
message=message)
......@@ -155,14 +155,18 @@ class Connection(base.Connection):
If not, we create it and return the record. This may result in a flush.
"""
if session is None:
session = self._engine_facade.get_session()
with session.begin(subtransactions=True):
et = session.query(models.EventType).filter(
models.EventType.desc == event_type).first()
if not et:
et = models.EventType(event_type)
session.add(et)
try:
if session is None:
session = self._engine_facade.get_session()
with session.begin(subtransactions=True):
et = session.query(models.EventType).filter(
models.EventType.desc == event_type).first()
if not et:
et = models.EventType(event_type)
session.add(et)
except dbexc.DBDuplicateEntry:
et = self._get_or_create_event_type(event_type, session)
return et
def record_events(self, event_models):
......
......@@ -17,6 +17,7 @@ import abc
from debtcollector import moves
from oslo_log import log
from oslo_utils import timeutils
import six
from ceilometer.i18n import _LW
......@@ -188,3 +189,42 @@ class BitfieldTraitPlugin(TraitPluginBase):
else:
bitfield |= bit
return [bitfield]
class TimedeltaPluginMissedFields(Exception):
def __init__(self):
msg = ('It is required to use two timestamp field with Timedelta '
'plugin.')
super(TimedeltaPluginMissedFields, self).__init__(msg)
class TimedeltaPlugin(TraitPluginBase):
"""Setup timedelta meter volume of two timestamps fields.
Example::
trait's fields definition: ['payload.created_at',
'payload.launched_at']
value is been created as total seconds between 'launched_at' and
'created_at' timestamps.
"""
# TODO(idegtiarov): refactor code to have meter_plugins separate from
# trait_plugins
def trait_value(self, match_list):
if len(match_list) != 2:
LOG.warning(_LW('Timedelta plugin is required two timestamp fields'
' to create timedelta value.'))
return
start, end = match_list
try:
start_time = timeutils.parse_isotime(start[1])
end_time = timeutils.parse_isotime(end[1])
except Exception as err:
LOG.warning(_LW('Failed to parse date from set fields, both '
'fields %(start)s and %(end)s must be datetime: '
'%(err)s') %
dict(start=start[0], end=end[0], err=err)
)
return
return abs((end_time - start_time).total_seconds())
......@@ -44,4 +44,7 @@ EXCHANGE_OPTS = [
cfg.StrOpt('zaqar_control_exchange',
default='zaqar',
help="Exchange name for Messaging service notifications."),
cfg.StrOpt('dns_control_exchange',
default='central',
help="Exchange name for DNS service notifications."),
]
......@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: ceilometer 6.0.0.0b3.dev49\n"
"Project-Id-Version: ceilometer 6.0.0.0b4.dev6\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-02-08 06:51+0000\n"
"POT-Creation-Date: 2016-03-07 06:08+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -22,26 +22,26 @@ msgstr ""
msgid "Dispatcher failed to handle the %s, requeue it."
msgstr ""
#: ceilometer/coordination.py:51
#: ceilometer/coordination.py:50
msgid ""
"Group ID: %{group_id}s, Members: %{members}s, Me: %{me}s: Current agent "
"is not part of group and cannot take tasks"
msgstr ""
#: ceilometer/coordination.py:89 ceilometer/coordination.py:101
#: ceilometer/coordination.py:88 ceilometer/coordination.py:100
msgid "Error connecting to coordination backend."
msgstr ""
#: ceilometer/coordination.py:116
#: ceilometer/coordination.py:115
msgid "Error sending a heartbeat to coordination backend."
msgstr ""
#: ceilometer/coordination.py:147
#: ceilometer/coordination.py:146
#, python-format
msgid "Error joining partitioning group %s, re-trying"
msgstr ""
#: ceilometer/coordination.py:199
#: ceilometer/coordination.py:198
msgid "Error getting group membership info from coordination backend."
msgstr ""
......@@ -55,7 +55,7 @@ msgstr ""
msgid "Unable to load changed event pipeline: %s"
msgstr ""
#: ceilometer/agent/manager.py:468
#: ceilometer/agent/manager.py:467
#, python-format
msgid "Skipping %(name)s, keystone issue: %(exc)s"
msgstr ""
......@@ -89,20 +89,25 @@ msgstr ""
msgid "Error processing event and it will be dropped: %s"
msgstr ""
#: ceilometer/dispatcher/gnocchi.py:107
#: ceilometer/dispatcher/gnocchi.py:102
#, python-format
msgid "Required field %s not specified"
msgstr ""
#: ceilometer/dispatcher/gnocchi.py:110
#: ceilometer/dispatcher/gnocchi.py:105
#, python-format
msgid "Required field %(field)s should be a %(type)s"
msgstr ""
#: ceilometer/dispatcher/gnocchi.py:239
#: ceilometer/dispatcher/gnocchi.py:234
msgid "Failed to connect to Gnocchi."
msgstr ""
#: ceilometer/dispatcher/gnocchi.py:250
#, python-format
msgid "Failed to load resource due to error %s"
msgstr ""
#: ceilometer/dispatcher/http.py:126
msgid "Status Code: %{code}s. Failed todispatch event: %{event}s"
msgstr ""
......@@ -150,7 +155,7 @@ msgid "inspector call failed for %(ident)s host %(host)s: %(err)s"
msgstr ""
#: ceilometer/hardware/pollsters/generic.py:227
#: ceilometer/meter/notifications.py:192
#: ceilometer/meter/notifications.py:197
#, python-format
msgid "Error loading meter definition : %(err)s"
msgstr ""
......
......@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: ceilometer 6.0.0.0b3.dev49\n"
"Project-Id-Version: ceilometer 6.0.0.0b4.dev34\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-02-08 06:51+0000\n"
"POT-Creation-Date: 2016-03-11 06:17+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,7 +17,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.2.0\n"
#: ceilometer/coordination.py:187
#: ceilometer/coordination.py:186
msgid ""
"Cannot extract tasks because agent failed to join group properly. "
"Rejoining group."
......@@ -44,7 +44,7 @@ msgid ""
"will be dropped"
msgstr ""
#: ceilometer/agent/manager.py:453
#: ceilometer/agent/manager.py:452
#, python-format
msgid "Skipping %(name)s, %(service_type)s service is not registered in keystone"
msgstr ""
......@@ -80,20 +80,9 @@ msgid ""
"to aodh endpoint."
msgstr ""
#: ceilometer/cmd/polling.py:51
#: ceilometer/cmd/polling.py:55
#, python-format
msgid "Duplicated values: %s found in CLI options, auto de-duplidated"
msgstr ""
#: ceilometer/compute/pollsters/disk.py:163
#: ceilometer/compute/pollsters/disk.py:633
#: ceilometer/compute/pollsters/memory.py:51
#: ceilometer/compute/pollsters/memory.py:94
#: ceilometer/compute/pollsters/net.py:99
#, python-format
msgid ""
"Instance %(instance_id)s was shut off while getting samples of "
"%(pollster)s: %(exc)s"
msgid "Duplicated values: %s found in CLI options, auto de-duplicated"
msgstr ""
#: ceilometer/compute/pollsters/memory.py:56
......@@ -119,23 +108,41 @@ msgstr ""
msgid "event signature invalid, discarding event: %s"
msgstr ""
#: ceilometer/dispatcher/gnocchi.py:220
#: ceilometer/dispatcher/gnocchi.py:215
#, python-format
msgid "unable to configure oslo_cache: %s"
msgstr ""
#: ceilometer/event/trait_plugins.py:125
#: ceilometer/event/trait_plugins.py:126
#, python-format
msgid ""
"split plugin is deprecated, add \".`split(%(sep)s, %(segment)d, "
"%(max_split)d)`\" to your jsonpath instead"
msgstr ""
#: ceilometer/event/trait_plugins.py:216
msgid ""
"Timedelta plugin is required two timestamp fields to create timedelta "
"value."
msgstr ""
#: ceilometer/event/trait_plugins.py:224
#, python-format
msgid ""
"Failed to parse date from set fields, both fields %(start)s and %(end)s "
"must be d