Commit d3f37444 authored by Thomas Goirand's avatar Thomas Goirand

Merge tag '9.0.4' into debian/pike

ceilometer 9.0.4 release

meta:version: 9.0.4
meta:diff-start: -
meta:series: pike
meta:release-type: release
meta:pypi: no
meta:first: no
meta:release:Author: gord chung <gord@live.ca>
meta:release:Commit: gord chung <gord@live.ca>
meta:release:Change-Id: I84249994dc22ae9563496eeead2d86efc3be8171
meta:release:Code-Review+2: Doug Hellmann <doug@doughellmann.com>
meta:release:Workflow+1: Doug Hellmann <doug@doughellmann.com>
parents 7d0b28ef f1689396
- job:
name: ceilometer-dsvm-tempest-plugin-mongodb
parent: legacy-dsvm-base
run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mongodb/run.yaml
post-run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mongodb/post.yaml
timeout: 7800
required-projects:
- openstack-infra/devstack-gate
- openstack/ceilometer
- openstack/tempest
- job:
name: ceilometer-dsvm-tempest-plugin-mongodb-identity-v3-only
parent: legacy-dsvm-base
run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mongodb-identity-v3-only/run.yaml
post-run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mongodb-identity-v3-only/post.yaml
timeout: 7800
required-projects:
- openstack-infra/devstack-gate
- openstack/ceilometer
- openstack/tempest
- job:
name: ceilometer-dsvm-tempest-plugin-mysql
parent: legacy-dsvm-base
run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mysql/run.yaml
post-run: playbooks/legacy/ceilometer-dsvm-tempest-plugin-mysql/post.yaml
timeout: 7800
required-projects:
- openstack-infra/devstack-gate
- openstack/ceilometer
- openstack/tempest
- job:
name: ceilometer-tox-py27-mongodb
parent: legacy-base
run: playbooks/legacy/ceilometer-tox-py27-mongodb/run.yaml
post-run: playbooks/legacy/ceilometer-tox-py27-mongodb/post.yaml
timeout: 2400
required-projects:
- openstack/requirements
- job:
name: ceilometer-tox-py27-mysql
parent: legacy-base
run: playbooks/legacy/ceilometer-tox-py27-mysql/run.yaml
post-run: playbooks/legacy/ceilometer-tox-py27-mysql/post.yaml
timeout: 2400
required-projects:
- openstack/requirements
- job:
name: ceilometer-tox-py27-postgresql
parent: legacy-base
run: playbooks/legacy/ceilometer-tox-py27-postgresql/run.yaml
post-run: playbooks/legacy/ceilometer-tox-py27-postgresql/post.yaml
timeout: 2400
required-projects:
- openstack/requirements
- job:
name: grenade-dsvm-ceilometer
parent: legacy-dsvm-base
run: playbooks/legacy/grenade-dsvm-ceilometer/run.yaml
post-run: playbooks/legacy/grenade-dsvm-ceilometer/post.yaml
timeout: 10800
required-projects:
- openstack-dev/grenade
- openstack-infra/devstack-gate
- openstack/ceilometer
- job:
name: telemetry-dsvm-integration-ceilometer
parent: legacy-dsvm-base
run: playbooks/legacy/telemetry-dsvm-integration-ceilometer/run.yaml
post-run: playbooks/legacy/telemetry-dsvm-integration-ceilometer/post.yaml
timeout: 7800
required-projects:
- openstack-infra/devstack-gate
- openstack/aodh
- openstack/ceilometer
- openstack/panko
# following are required when DEVSTACK_GATE_HEAT, which this
# job turns on
- openstack/dib-utils
- openstack/diskimage-builder
- project:
name: openstack/ceilometer
check:
jobs:
- ceilometer-dsvm-tempest-plugin-mongodb
- ceilometer-dsvm-tempest-plugin-mysql
- ceilometer-tox-py27-mongodb
- ceilometer-tox-py27-mysql
- ceilometer-tox-py27-postgresql
- grenade-dsvm-ceilometer:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^setup.cfg$
- telemetry-dsvm-integration-ceilometer
- ceilometer-dsvm-tempest-plugin-mongodb-identity-v3-only
# TripleO jobs that deploy Telemetry.
# Note we don't use a project-template here, so it's easier
# to disable voting on one specific job if things go wrong.
# tripleo-ci-centos-7-scenario00(1|2)-multinode-oooq will only
# run on stable/pike while the -container will run in Queens
# and beyond.
# If you need any support to debug these jobs in case of
# failures, please reach us on #tripleo IRC channel.
- tripleo-ci-centos-7-scenario001-multinode-oooq:
voting: false
- tripleo-ci-centos-7-scenario001-multinode-oooq-container:
voting: false
- tripleo-ci-centos-7-scenario002-multinode-oooq:
voting: false
- tripleo-ci-centos-7-scenario002-multinode-oooq-container:
voting: false
gate:
jobs:
- ceilometer-dsvm-tempest-plugin-mongodb
- ceilometer-dsvm-tempest-plugin-mysql
- ceilometer-tox-py27-mongodb
- ceilometer-tox-py27-mysql
- ceilometer-tox-py27-postgresql
- grenade-dsvm-ceilometer:
irrelevant-files:
- ^(test-|)requirements.txt$
- ^setup.cfg$
- telemetry-dsvm-integration-ceilometer
- ceilometer-dsvm-tempest-plugin-mongodb-identity-v3-only
......@@ -108,6 +108,7 @@ class Resources(object):
static_resources_group = self.agent_manager.construct_group_id(
utils.hash_of_set(self._resources))
return [v for v in self._resources if
not self.agent_manager.partition_coordinator or
self.agent_manager.hashrings[
static_resources_group].belongs_to_self(
six.text_type(v))] + source_discovery
......@@ -279,13 +280,13 @@ class AgentManager(cotyledon.Service):
self.discoveries = list(itertools.chain(*list(discoveries)))
self.polling_periodics = None
self.hashrings = None
self.partition_coordinator = None
if self.conf.coordination.backend_url:
# XXX uuid4().bytes ought to work, but it requires ascii for now
coordination_id = str(uuid.uuid4()).encode('ascii')
self.partition_coordinator = coordination.get_coordinator(
self.conf.coordination.backend_url, coordination_id)
else:
self.partition_coordinator = None
# Compose coordination group prefix.
# We'll use namespaces as the basement for this partitioning.
......
......@@ -145,12 +145,21 @@ class InstanceDiscovery(plugin_base.DiscoveryBase):
def discover_libvirt_polling(self, manager, param=None):
instances = []
for domain in self.connection.listAllDomains():
try:
xml_string = domain.metadata(
libvirt.VIR_DOMAIN_METADATA_ELEMENT,
"http://openstack.org/xmlns/libvirt/nova/1.0")
except libvirt.libvirtError as e:
if libvirt_utils.is_disconnection_exception(e):
# Re-raise the exception so it's handled and retries
raise
LOG.error(
"Fail to get domain uuid %s metadata, libvirtError: %s",
domain.UUIDString(), e.message)
continue
full_xml = etree.fromstring(domain.XMLDesc())
os_type_xml = full_xml.find("./os/type")
xml_string = domain.metadata(
libvirt.VIR_DOMAIN_METADATA_ELEMENT,
"http://openstack.org/xmlns/libvirt/nova/1.0")
metadata_xml = etree.fromstring(xml_string)
# TODO(sileht): We don't have the flavor ID here So the Gnocchi
......
......@@ -55,12 +55,14 @@ class MemorySwapInPollster(InstanceStatsPollster):
sample_name = 'memory.swap.in'
sample_unit = 'MB'
sample_stats_key = 'memory_swap_in'
sample_type = sample.TYPE_CUMULATIVE
class MemorySwapOutPollster(InstanceStatsPollster):
sample_name = 'memory.swap.out'
sample_unit = 'MB'
sample_stats_key = 'memory_swap_out'
sample_type = sample.TYPE_CUMULATIVE
class PerfCPUCyclesPollster(InstanceStatsPollster):
......
......@@ -119,7 +119,8 @@ class LibvirtInspector(virt_inspector.Inspector):
for device in filter(
bool,
[target.get("dev")
for target in tree.findall('devices/disk/target')]):
for target in tree.findall('devices/disk/target')
if target.getparent().find('source') is not None]):
block_stats = domain.blockStats(device)
yield virt_inspector.DiskStats(device=device,
read_requests=block_stats[0],
......@@ -133,27 +134,19 @@ class LibvirtInspector(virt_inspector.Inspector):
domain = self._get_domain_not_shut_off_or_raise(instance)
tree = etree.fromstring(domain.XMLDesc(0))
for disk in tree.findall('devices/disk'):
disk_type = disk.get('type')
if disk_type:
if disk_type == 'network':
LOG.warning(
'Inspection disk usage of network disk '
'%(instance_uuid)s unsupported by libvirt' % {
'instance_uuid': instance.id})
continue
# NOTE(lhx): "cdrom" device associated to the configdrive
# no longer has a "source" element. Releated bug:
# https://bugs.launchpad.net/ceilometer/+bug/1622718
if disk.find('source') is None:
continue
target = disk.find('target')
device = target.get('dev')
if device:
block_info = domain.blockInfo(device)
yield virt_inspector.DiskInfo(device=device,
capacity=block_info[0],
allocation=block_info[1],
physical=block_info[2])
# NOTE(lhx): "cdrom" device associated to the configdrive
# no longer has a "source" element. Releated bug:
# https://bugs.launchpad.net/ceilometer/+bug/1622718
if disk.find('source') is None:
continue
target = disk.find('target')
device = target.get('dev')
if device:
block_info = domain.blockInfo(device)
yield virt_inspector.DiskInfo(device=device,
capacity=block_info[0],
allocation=block_info[1],
physical=block_info[2])
@libvirt_utils.raise_nodata_if_unsupported
@libvirt_utils.retry_on_disconnect
......
......@@ -161,11 +161,15 @@ class GenericHardwareDeclarativePollster(plugin_base.PollsterBase):
parsed_url,
i_cache[identifier]))
except Exception as err:
LOG.exception('inspector call failed for %(ident)s '
'host %(host)s: %(err)s',
dict(ident=identifier,
host=parsed_url.hostname,
err=err))
msg = ('inspector call failed for %(ident)s '
'host %(host)s: %(err)s' %
dict(ident=identifier,
host=parsed_url.hostname,
err=err))
if "timeout" in str(err):
LOG.warning(msg)
else:
LOG.exception(msg)
return itertools.chain(*sample_iters)
def generate_samples(self, host_url, data):
......
......@@ -13,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
import oslo_messaging
from oslo_messaging._drivers import impl_rabbit
from oslo_messaging.notify import notifier
from oslo_messaging import serializer as oslo_serializer
......@@ -23,6 +25,19 @@ TRANSPORTS = {}
def setup():
oslo_messaging.set_transport_defaults('ceilometer')
# NOTE(sileht): When batch is not enabled, oslo.messaging read all messages
# in the queue and can consume a lot of memory, that works for rpc because
# you never have a lot of message, but sucks for notification. The
# default is not changeable on oslo.messaging side. And we can't expose
# this option to set set_transport_defaults because it a driver option.
# 100 allow to prefetch a lot of messages but limit memory to 1G per
# workers in worst case (~ 1M Nova notification)
# And even driver options are located in private module, this is not going
# to break soon.
cfg.set_defaults(
impl_rabbit.rabbit_opts,
rabbit_qos_prefetch_count=100,
)
def get_transport(conf, url=None, optional=False, cache=True):
......
......@@ -26,7 +26,6 @@ from futurist import periodics
from oslo_config import cfg
from oslo_log import log
import oslo_messaging
import six
from stevedore import extension
from tooz import coordination
......@@ -128,6 +127,9 @@ class NotificationService(cotyledon.Service):
str(uuid.uuid4()).encode('ascii'))
self.partition_coordinator = coordination.get_coordinator(
self.conf.coordination.backend_url, coordination_id)
self.partition_set = list(range(
self.conf.notification.pipeline_processing_queues))
self.group_state = None
else:
self.partition_coordinator = None
......@@ -190,7 +192,7 @@ class NotificationService(cotyledon.Service):
self.transport = messaging.get_transport(self.conf)
if self.conf.notification.workload_partitioning:
self.partition_coordinator.start()
self.partition_coordinator.start(start_heart=True)
else:
# FIXME(sileht): endpoint uses the notification_topics option
# and it should not because this is an oslo_messaging option
......@@ -214,16 +216,15 @@ class NotificationService(cotyledon.Service):
run_immediately=True)
def run_watchers():
self.partition_coordinator.run_watchers()
if self.group_state != self.hashring.ring.nodes:
self.group_state = self.hashring.ring.nodes.copy()
self._refresh_agent()
self.periodic = periodics.PeriodicWorker.create(
[], executor_factory=lambda:
futures.ThreadPoolExecutor(max_workers=10))
self.periodic.add(run_watchers)
utils.spawn_thread(self.periodic.start)
# configure pipelines after all coordination is configured.
with self.coord_lock:
self._configure_pipeline_listener()
def _configure_main_queue_listeners(self, pipe_manager,
event_pipe_manager):
......@@ -266,7 +267,7 @@ class NotificationService(cotyledon.Service):
)
self.listeners.append(listener)
def _refresh_agent(self, event):
def _refresh_agent(self):
with self.coord_lock:
if self.shutdown:
# NOTE(sileht): We are going to shutdown we everything will be
......@@ -278,13 +279,8 @@ class NotificationService(cotyledon.Service):
ev_pipes = self.event_pipeline_manager.pipelines
pipelines = self.pipeline_manager.pipelines + ev_pipes
transport = messaging.get_transport(self.conf)
partitioned = six.moves.range(
self.conf.notification.pipeline_processing_queues
)
if self.partition_coordinator:
partitioned = list(filter(
self.hashring.belongs_to_self, partitioned))
partitioned = list(filter(
self.hashring.belongs_to_self, self.partition_set))
endpoints = []
targets = []
......
......@@ -18,6 +18,7 @@
import abc
import itertools
import operator
import threading
from oslo_config import cfg
from oslo_log import log
......@@ -84,6 +85,7 @@ class MessagingPublisher(publisher.ConfigPublisherBase):
'max_queue_length', [1024])[-1])
self.max_retry = 0
self.queue_lock = threading.Lock()
self.local_queue = []
if self.policy in ['default', 'queue', 'drop']:
......@@ -123,17 +125,16 @@ class MessagingPublisher(publisher.ConfigPublisherBase):
self.flush()
def flush(self):
# NOTE(sileht):
# this is why the self.local_queue is emptied before processing the
# queue and the remaining messages in the queue are added to
# self.local_queue after in case of another call having already added
# something in the self.local_queue
queue = self.local_queue
self.local_queue = []
self.local_queue = (self._process_queue(queue, self.policy) +
self.local_queue)
if self.policy == 'queue':
self._check_queue_length()
with self.queue_lock:
queue = self.local_queue
self.local_queue = []
queue = self._process_queue(queue, self.policy)
with self.queue_lock:
self.local_queue = (queue + self.local_queue)
if self.policy == 'queue':
self._check_queue_length()
def _check_queue_length(self):
queue_length = len(self.local_queue)
......
......@@ -16,16 +16,9 @@
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urllib
from tempest import clients
from tempest import config
from tempest.lib.common import rest_client
from tempest.lib.services.compute import flavors_client as flavor_cli
from tempest.lib.services.compute import floating_ips_client as floatingip_cli
from tempest.lib.services.compute import networks_client as network_cli
from tempest.lib.services.compute import servers_client as server_cli
from tempest.lib.services.image.v2 import images_client as img_cli_v2
from tempest import manager
from tempest.services.object_storage import container_client as container_cli
from tempest.services.object_storage import object_client as obj_cli
CONF = config.CONF
......@@ -89,43 +82,9 @@ class TelemetryClient(rest_client.RestClient):
return rest_client.ResponseBody(resp, body)
class Manager(manager.Manager):
load_clients = [
'servers_client',
'compute_networks_client',
'compute_floating_ips_client',
'flavors_client',
'image_client_v2',
'telemetry_client',
'container_client',
'object_client',
]
default_params = {
'disable_ssl_certificate_validation':
CONF.identity.disable_ssl_certificate_validation,
'ca_certs': CONF.identity.ca_certificates_file,
'trace_requests': CONF.debug.trace_requests
}
class Manager(clients.Manager):
compute_params = {
'service': CONF.compute.catalog_type,
'region': CONF.compute.region or CONF.identity.region,
'endpoint_type': CONF.compute.endpoint_type,
'build_interval': CONF.compute.build_interval,
'build_timeout': CONF.compute.build_timeout,
}
compute_params.update(default_params)
image_params = {
'service': CONF.image.catalog_type,
'region': CONF.image.region or CONF.identity.region,
'endpoint_type': CONF.image.endpoint_type,
'build_interval': CONF.image.build_interval,
'build_timeout': CONF.image.build_timeout,
}
image_params.update(default_params)
default_params = config.service_client_config()
telemetry_params = {
'service': CONF.telemetry.catalog_type,
......@@ -134,53 +93,18 @@ class Manager(manager.Manager):
}
telemetry_params.update(default_params)
object_storage_params = {
'service': CONF.object_storage.catalog_type,
'region': CONF.object_storage.region or CONF.identity.region,
'endpoint_type': CONF.object_storage.endpoint_type
}
object_storage_params.update(default_params)
def __init__(self, credentials=None, service=None):
super(Manager, self).__init__(credentials)
for client in self.load_clients:
getattr(self, 'set_%s' % client)()
def set_servers_client(self):
self.servers_client = server_cli.ServersClient(
self.auth_provider,
**self.compute_params)
def set_compute_networks_client(self):
self.compute_networks_client = network_cli.NetworksClient(
self.auth_provider,
**self.compute_params)
def set_compute_floating_ips_client(self):
self.compute_floating_ips_client = floatingip_cli.FloatingIPsClient(
self.auth_provider,
**self.compute_params)
def set_flavors_client(self):
self.flavors_client = flavor_cli.FlavorsClient(
self.auth_provider,
**self.compute_params)
def set_image_client_v2(self):
self.image_client_v2 = img_cli_v2.ImagesClient(
self.auth_provider,
**self.image_params)
def __init__(self, credentials):
# TODO(andreaf) Overriding Manager is a workaround. The "proper" way
# would it to expose the ceilometer service client via the plugin
# interface, use tempest.lib.clients and tempest master.
# Then ceilometer service client would be loaded and configured
# automatically into ServiceClients.
# In any case we're about to declare clients.Manager a stable
# interface for plugins and we won't change it, so this code won't
# break.
super(Manager, self).__init__(credentials=credentials)
self.set_telemetry_client()
def set_telemetry_client(self):
self.telemetry_client = TelemetryClient(self.auth_provider,
**self.telemetry_params)
def set_container_client(self):
self.container_client = container_cli.ContainerClient(
self.auth_provider,
**self.object_storage_params)
def set_object_client(self):
self.object_client = obj_cli.ObjectClient(
self.auth_provider,
**self.object_storage_params)
......@@ -15,6 +15,12 @@ import datetime
import fixtures
import iso8601
import mock
import testtools
try:
import libvirt
except ImportError:
libvirt = None
from ceilometer.compute import discovery
from ceilometer.compute.pollsters import util
......@@ -58,6 +64,21 @@ LIBVIRT_DESC_XML = """
</domain>
"""
LIBVIRT_MANUAL_INSTANCE_DESC_XML = """
<domain type='kvm' id='1'>
<name>Manual-instance-00000001</name>
<uuid>5e637d0d-8c0e-441a-a11a-a9dc95aed84e</uuid>
<os>
<type arch='x86_64' machine='pc-i440fx-xenial'>hvm</type>
<kernel>/opt/instances/5e637d0d-8c0e-441a-a11a-a9dc95aed84e/kernel</kernel>
<initrd>/opt/instances/5e637d0d-8c0e-441a-a11a-a9dc95aed84e/ramdisk</initrd>
<cmdline>root=/dev/vda console=tty0 console=ttyS0</cmdline>
<boot dev='hd'/>
<smbios mode='sysinfo'/>
</os>
</domain>
"""
class FakeDomain(object):
def state(self):
......@@ -81,6 +102,33 @@ class FakeConn(object):
return [FakeDomain()]
class FakeManualInstanceDomain(object):
def state(self):
return [1, 2]
def name(self):
return "Manual-instance-00000001"
def UUIDString(self):
return "5e637d0d-8c0e-441a-a11a-a9dc95aed84e"
def XMLDesc(self):
return LIBVIRT_MANUAL_INSTANCE_DESC_XML
def metadata(self, flags, url):
# Note(xiexianbin): vm not create by nova-compute don't have metadata
# elements like: '<nova:instance
# xmlns:nova="http://openstack.org/xmlns/libvirt/nova/1.0">'
# When invoke get metadata method, raise libvirtError.
raise libvirt.libvirtError(
"metadata not found: Requested metadata element is not present")
class FakeManualInstanceConn(object):
def listAllDomains(self):
return [FakeManualInstanceDomain()]
class TestDiscovery(base.BaseTestCase):
def setUp(self):
......@@ -236,3 +284,16 @@ class TestDiscovery(base.BaseTestCase):
mock.call('test', None)]
self.assertEqual(expected_calls,
self.client.instance_get_all_by_host.call_args_list)
@testtools.skipUnless(libvirt, "libvirt not available")
@mock.patch.object(utils, "libvirt")
@mock.patch.object(discovery, "libvirt")
def test_discovery_with_libvirt_error(self, libvirt, libvirt2):
self.CONF.set_override("instance_discovery_method",
"libvirt_metadata",
group="compute")
libvirt.VIR_DOMAIN_METADATA_ELEMENT = 2
libvirt2.openReadOnly.return_value = FakeManualInstanceConn()
dsc = discovery.InstanceDiscovery(self.CONF)
resources = dsc.discover(mock.MagicMock())
self.assertEqual(0, len(resources))
......@@ -367,7 +367,7 @@ class TestLibvirtInspection(base.BaseTestCase):
with mock.patch('ceilometer.compute.virt.libvirt.utils.'
'refresh_libvirt_connection', return_value=conn):
disks = list(self.inspector.inspect_disk_info(self.instance, None))
self.assertEqual(0, len(disks))
self.assertEqual(1, len(disks))
def test_inspect_disk_info_without_source_element(self):
dom_xml = """
......@@ -397,6 +397,44 @@ class TestLibvirtInspection(base.BaseTestCase):
disks = list(self.inspector.inspect_disk_info(self.instance, None))
self.assertEqual(0, len(disks))
def test_inspect_disks_without_source_element(self):
dom_xml = """
<domain type='kvm'>
<devices>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw' cache='none'/>
<backingStore/>
<target dev='hdd' bus='ide' tray='open'/>
<readonly/>
<alias name='ide0-1-1'/>
<address type='drive' controller='0' bus='1'
target='0' unit='1'/>
</disk>
</devices>
</domain>
"""
blockStatsFlags = {'wr_total_times': 91752302267,
'rd_operations': 6756,
'flush_total_times': 1310427331,
'rd_total_times': 29142253616,
'rd_bytes': 171460096,
'flush_operations': 746,
'wr_operations': 1437,
'wr_bytes': 13574656}
domain = mock.Mock()
domain.XMLDesc.return_value = dom_xml
domain.info.return_value = (0, 0, 0, 2, 999999)
domain.blockStats.return_value = (1, 2, 3, 4, -1)
domain.blockStatsFlags.return_value = blockStatsFlags
conn = mock.Mock()
conn.lookupByUUIDString.return_value = domain
with mock.patch('ceilometer.compute.virt.libvirt.utils.'
'refresh_libvirt_connection', return_value=conn):
disks = list(self.inspector.inspect_disks(self.instance, None))
self.assertEqual(0, len(disks))
def test_inspect_memory_usage_with_domain_shutoff(self):
domain = mock.Mock()
domain.info.return_value = (5, 0, 51200, 2, 999999)
......
......@@ -199,11 +199,13 @@ function ceilometer_create_accounts {
function install_gnocchi {
echo_summary "Installing Gnocchi"
if [ $GNOCCHI_GIT_PATH ]; then
pip_install -e $GNOCCHI_GIT_PATH[redis,${DATABASE_TYPE},keystone] uwsgi
if python3_enabled; then
PY_VERS=${PYTHON3_VERSION}