Added hardware pollsters for the central agent

This patch added various pollsters for the hardware agent.

Implements: blueprint monitoring-physical-devices

Based on the original code at https://github.com/graflu0/ceilometer
contributed by Lucas and Toni.

Signed-off-by: Lucas Graf <graflu0@students.zhaw.ch>
Signed-off-by: Toni Zehnder <zehndton@students.zhaw.ch>
Signed-off-by: Lianhao Lu <lianhao.lu@intel.com>

Change-Id: Id259b95efdbf0b7d03b657586d588f9637d9a367
This commit is contained in:
Lianhao Lu 2013-08-21 16:23:17 +08:00
parent 4e72e0273f
commit 3fec862c86
14 changed files with 657 additions and 0 deletions

View File

@ -0,0 +1,107 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
"""Base class for plugins used by the hardware agent."""
import abc
import six
from ceilometer.central import plugin
from ceilometer.hardware import inspector as insloader
from ceilometer.openstack.common.gettextutils import _ # noqa
from ceilometer.openstack.common import log
from ceilometer.openstack.common import network_utils
LOG = log.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class HardwarePollster(plugin.CentralPollster):
"""Base class for plugins that support the polling API."""
CACHE_KEY = None
INSPECT_METHOD = None
def __init__(self):
super(HardwarePollster, self).__init__()
self.inspectors = {}
def get_samples(self, manager, cache, resources=[]):
"""Return an iterable of Sample instances from polling the resources.
:param manager: The service manager invoking the plugin
:param cache: A dictionary for passing data between plugins
:param resources: end point to poll data from
"""
h_cache = cache.setdefault(self.CACHE_KEY, {})
for res in resources:
parsed_url = network_utils.urlsplit(res)
inspector = self._get_inspector(parsed_url)
func = getattr(inspector, self.INSPECT_METHOD)
try:
# Call hardware inspector to poll for the data
i_cache = h_cache.setdefault(res, {})
if res not in i_cache:
i_cache[res] = list(func(parsed_url))
# Generate samples
if i_cache[res]:
return self.generate_samples(parsed_url, i_cache[res])
except Exception as err:
LOG.exception(_('inspector call %(func)r failed for '
'host %(host)s: %(err)s'),
dict(func=func,
host=parsed_url.hostname,
err=err))
# if no resources, we still need to return an iterable
# because of the interface requirement
return ()
def generate_samples(self, host_url, datas):
"""Generate an interable Sample from the datas returned by inspector
:param host_url: host url of the endpoint
:param datas: list of data returned by the corresponding inspector
"""
return (self.generate_one_sample(host_url, data) for data in datas)
@abc.abstractmethod
def generate_one_sample(self, host_url, c_data):
"""Return one Sample.
:param host_url: host url of the endpoint
:param c_data: data returned by the corresponding inspector, of
one of the types defined in the file
ceiloemter.hardware.inspector.base.CPUStats
"""
def _get_inspector(self, parsed_url):
if parsed_url.scheme not in self.inspectors:
try:
driver = insloader.get_inspector(parsed_url)
self.inspectors[parsed_url.scheme] = driver
except Exception as err:
LOG.exception(_("Can NOT load inspector %(name)s: %(err)s"),
dict(name=parsed_url.scheme,
err=err))
raise err
return self.inspectors[parsed_url.scheme]

View File

@ -0,0 +1,66 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware import plugin
from ceilometer.hardware.pollsters import util
from ceilometer import sample
class _Base(plugin.HardwarePollster):
CACHE_KEY = 'cpu'
INSPECT_METHOD = 'inspect_cpu'
class CPUUtil1MinPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
return util.make_sample_from_host(host,
name='cpu.util.1min',
type=sample.TYPE_GAUGE,
unit='%',
volume=c_data.cpu_1_min,
)
class CPUUtil5MinPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
return util.make_sample_from_host(host,
name='cpu.util.5min',
type=sample.TYPE_GAUGE,
unit='%',
volume=c_data.cpu_5_min,
)
class CPUUtil15MinPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
return util.make_sample_from_host(host,
name='cpu.util.15min',
type=sample.TYPE_GAUGE,
unit='%',
volume=c_data.cpu_15_min,
)

View File

@ -0,0 +1,58 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware import plugin
from ceilometer.hardware.pollsters import util
from ceilometer import sample
class _Base(plugin.HardwarePollster):
CACHE_KEY = 'disk'
INSPECT_METHOD = 'inspect_disk'
class DiskTotalPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(disk, info) = c_data
return util.make_sample_from_host(host,
name='disk.size.total',
type=sample.TYPE_GAUGE,
unit='B',
volume=info.size,
res_metadata=disk,
)
class DiskUsedPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(disk, info) = c_data
return util.make_sample_from_host(host,
name='disk.size.used',
type=sample.TYPE_GAUGE,
unit='B',
volume=info.used,
res_metadata=disk,
)

View File

@ -0,0 +1,54 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware import plugin
from ceilometer.hardware.pollsters import util
from ceilometer import sample
class _Base(plugin.HardwarePollster):
CACHE_KEY = 'memory'
INSPECT_METHOD = 'inspect_memory'
class MemoryTotalPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
return util.make_sample_from_host(host,
name='memory.total',
type=sample.TYPE_GAUGE,
unit='B',
volume=c_data.total,
)
class MemoryUsedPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
return util.make_sample_from_host(host,
name='memory.used',
type=sample.TYPE_GAUGE,
unit='B',
volume=c_data.used,
)

View File

@ -0,0 +1,86 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware import plugin
from ceilometer.hardware.pollsters import util
from ceilometer import sample
class _Base(plugin.HardwarePollster):
CACHE_KEY = 'nic'
INSPECT_METHOD = 'inspect_network'
class BandwidthBytesPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(nic, info) = c_data
return util.make_sample_from_host(host,
name='network.bandwidth.bytes',
type=sample.TYPE_CUMULATIVE,
unit='B',
volume=info.bandwidth,
res_metadata=nic,
)
class IncomingBytesPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(nic, info) = c_data
return util.make_sample_from_host(host,
name='network.incoming.bytes',
type=sample.TYPE_CUMULATIVE,
unit='B',
volume=info.rx_bytes,
res_metadata=nic,
)
class OutgoingBytesPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(nic, info) = c_data
return util.make_sample_from_host(host,
name='network.outgoing.bytes',
type=sample.TYPE_CUMULATIVE,
unit='B',
volume=info.tx_bytes,
res_metadata=nic,
)
class OutgoingErrorsPollster(_Base):
@staticmethod
def generate_one_sample(host, c_data):
(nic, info) = c_data
return util.make_sample_from_host(host,
name='network.outgoing.errors',
type=sample.TYPE_CUMULATIVE,
unit='packet',
volume=info.error,
res_metadata=nic,
)

View File

@ -0,0 +1,52 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 ZHAW SoE
# Copyright © 2014 Intel Corp.
#
# Authors: Lucas Graf <graflu0@students.zhaw.ch>
# Toni Zehnder <zehndton@students.zhaw.ch>
# Lianhao Lu <lianhao.lu@intel.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.
import copy
import urlparse
from ceilometer.openstack.common import timeutils
from ceilometer import sample
def get_metadata_from_host(host_url):
return {'resource_url': urlparse.urlunsplit(host_url)}
def make_sample_from_host(host_url, name, type, unit, volume,
project_id=None, user_id=None, res_metadata=None):
resource_metadata = dict()
if res_metadata is not None:
metadata = copy.copy(res_metadata)
resource_metadata = dict(zip(metadata._fields, metadata))
resource_metadata.update(get_metadata_from_host(host_url))
return sample.Sample(
name=name,
type=type,
unit=unit,
volume=volume,
user_id=project_id,
project_id=user_id,
resource_id=host_url.hostname,
timestamp=timeutils.isotime(),
resource_metadata=resource_metadata,
source='hardware',
)

View File

@ -0,0 +1,81 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Intel Corp
#
# Authors: Lianhao Lu <lianhao.lu@intel.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.
import fixtures
import mock
from ceilometer.central import manager
from ceilometer.hardware.inspector import base as inspector_base
from ceilometer.tests import base as test_base
class FakeInspector(inspector_base.Inspector):
CPU = inspector_base.CPUStats(cpu_1_min=0.99,
cpu_5_min=0.77,
cpu_15_min=0.55)
DISK = (inspector_base.Disk(device='/dev/sda1', path='/'),
inspector_base.DiskStats(size=1000, used=90))
MEMORY = inspector_base.MemoryStats(total=1000, used=90)
NET = (inspector_base.Interface(name='test.teest',
mac='001122334455',
ip='10.0.0.2'),
inspector_base.InterfaceStats(bandwidth=1000,
rx_bytes=90,
tx_bytes=80,
error=1))
def inspect_cpu(self, host):
yield self.CPU
def inspect_disk(self, host):
yield self.DISK
def inspect_memory(self, host):
yield self.MEMORY
def inspect_network(self, host):
yield self.NET
class TestPollsterBase(test_base.BaseTestCase):
def faux_get_inspector(url, namespace=None):
return FakeInspector()
def setUp(self):
super(TestPollsterBase, self).setUp()
self.host = ["test://test"]
self.useFixture(fixtures.MonkeyPatch(
'ceilometer.hardware.inspector.get_inspector',
self.faux_get_inspector))
@mock.patch('ceilometer.pipeline.setup_pipeline', mock.MagicMock())
def _check_get_samples(self, factory, name,
expected_value, expected_type):
mgr = manager.AgentManager()
pollster = factory()
cache = {}
samples = list(pollster.get_samples(mgr, cache, self.host))
self.assertTrue(samples)
self.assertIn(pollster.CACHE_KEY, cache)
self.assertIn(self.host[0], cache[pollster.CACHE_KEY])
self.assertEqual(set([s.name for s in samples]),
set([name]))
match = [s for s in samples if s.name == name]
self.assertEqual(match[0].volume, expected_value)
self.assertEqual(match[0].type, expected_type)

View File

@ -0,0 +1,35 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Intel Corp
#
# Authors: Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware.pollsters import cpu
from ceilometer import sample
from ceilometer.tests.hardware.pollsters import base
class TestCPUPollsters(base.TestPollsterBase):
def test_1min(self):
self._check_get_samples(cpu.CPUUtil1MinPollster, 'cpu.util.1min',
0.99, sample.TYPE_GAUGE)
def test_5min(self):
self._check_get_samples(cpu.CPUUtil5MinPollster, 'cpu.util.5min',
0.77, sample.TYPE_GAUGE)
def test_15min(self):
self._check_get_samples(cpu.CPUUtil15MinPollster, 'cpu.util.15min',
0.55, sample.TYPE_GAUGE)

View File

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Intel Corp
#
# Authors: Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware.pollsters import disk
from ceilometer import sample
from ceilometer.tests.hardware.pollsters import base
class TestDiskPollsters(base.TestPollsterBase):
def test_disk_size_total(self):
self._check_get_samples(disk.DiskTotalPollster, 'disk.size.total',
1000, sample.TYPE_GAUGE)
def test_disk_size_used(self):
self._check_get_samples(disk.DiskUsedPollster, 'disk.size.used',
90, sample.TYPE_GAUGE)

View File

@ -0,0 +1,33 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Intel Corp
#
# Authors: Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware.pollsters import memory
from ceilometer import sample
from ceilometer.tests.hardware.pollsters import base
class TestMemoryPollsters(base.TestPollsterBase):
def test_memory_size_total(self):
self._check_get_samples(memory.MemoryTotalPollster,
'memory.total',
1000, sample.TYPE_GAUGE)
def test_memory_size_used(self):
self._check_get_samples(memory.MemoryUsedPollster,
'memory.used',
90, sample.TYPE_GAUGE)

View File

@ -0,0 +1,43 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2013 Intel Corp
#
# Authors: Lianhao Lu <lianhao.lu@intel.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.
from ceilometer.hardware.pollsters import net
from ceilometer import sample
from ceilometer.tests.hardware.pollsters import base
class TestNetPollsters(base.TestPollsterBase):
def test_bandwidth(self):
self._check_get_samples(net.BandwidthBytesPollster,
'network.bandwidth.bytes',
1000, sample.TYPE_CUMULATIVE)
def test_incoming(self):
self._check_get_samples(net.IncomingBytesPollster,
'network.incoming.bytes',
90, sample.TYPE_CUMULATIVE)
def test_outgoing(self):
self._check_get_samples(net.OutgoingBytesPollster,
'network.outgoing.bytes',
80, sample.TYPE_CUMULATIVE)
def test_error(self):
self._check_get_samples(net.OutgoingErrorsPollster,
'network.outgoing.errors',
1, sample.TYPE_CUMULATIVE)

View File

@ -111,6 +111,17 @@ ceilometer.poll.central =
switch.flow.duration.nanoseconds = ceilometer.network.statistics.flow:FlowPollsterDurationNanoseconds
switch.flow.duration.seconds = ceilometer.network.statistics.flow:FlowPollsterDurationSeconds
switch.flow.packets = ceilometer.network.statistics.flow:FlowPollsterPackets
hardware.cpu.util.1min = ceilometer.hardware.pollsters.cpu:CPUUtil1MinPollster
hardware.cpu.util.5min = ceilometer.hardware.pollsters.cpu:CPUUtil5MinPollster
hardware.cpu.util.15min = ceilometer.hardware.pollsters.cpu:CPUUtil15MinPollster
hardware.disk.size.total = ceilometer.hardware.pollsters.disk:DiskTotalPollster
hardware.disk.size.used = ceilometer.hardware.pollsters.disk:DiskUsedPollster
hardware.network.bandwidth.bytes = ceilometer.hardware.pollsters.net:BandwidthBytesPollster
hardware.network.incoming.bytes = ceilometer.hardware.pollsters.net:IncomingBytesPollster
hardware.network.outgoing.bytes = ceilometer.hardware.pollsters.net:OutgoingBytesPollster
hardware.network.outgoing.errors = ceilometer.hardware.pollsters.net:OutgoingErrorsPollster
hardware.memory.total = ceilometer.hardware.pollsters.memory:MemoryTotalPollster
hardware.memory.used = ceilometer.hardware.pollsters.memory:MemoryUsedPollster
ceilometer.storage =