
All of the inspector interface methods takes in instance being inspected and duration of seconds over which metric needs to be calculated. Though the duration parameter is not used, the method signaures need to be updated to match ceilometer changes [1]. [1] 90560e42fd1b589ebb03b8629cf591dee8e559a6 Change-Id: I3ad8da0dc9cd26e269bf18f26231063d122faf98
437 lines
19 KiB
Python
437 lines
19 KiB
Python
# Copyright 2015, 2019 IBM Corp.
|
|
#
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 datetime
|
|
import mock
|
|
|
|
from ceilometer.compute.virt import inspector as virt_inspector
|
|
from oslotest import base
|
|
from pypowervm.helpers import log_helper as log_hlp
|
|
from pypowervm.helpers import vios_busy as vio_hlp
|
|
from pypowervm.tests import test_fixtures as api_fx
|
|
|
|
from ceilometer_powervm.compute.virt.powervm import inspector as p_inspect
|
|
from ceilometer_powervm.tests.compute.virt.powervm import pvm_fixtures
|
|
|
|
|
|
class TestPowerVMInspectorInit(base.BaseTestCase):
|
|
"""Tests the initialization of the VM Inspector."""
|
|
|
|
@mock.patch('pypowervm.tasks.monitor.util.LparMetricCache')
|
|
@mock.patch('pypowervm.tasks.monitor.util.ensure_ltm_monitors')
|
|
@mock.patch('ceilometer_powervm.compute.virt.powervm.inspector.'
|
|
'PowerVMInspector._get_host_uuid')
|
|
@mock.patch('pypowervm.adapter.Adapter')
|
|
@mock.patch('pypowervm.adapter.Session')
|
|
def test_init(self, mock_session, mock_adapter, mock_get_host_uuid,
|
|
mock_ensure_ltm, mock_cache):
|
|
# Mock up data
|
|
mock_get_host_uuid.return_value = 'host_uuid'
|
|
|
|
# Invoke
|
|
inspector = p_inspect.PowerVMInspector(None)
|
|
|
|
# Validate
|
|
mock_session.assert_called_once_with(conn_tries=300)
|
|
mock_adapter.assert_called_once_with(
|
|
mock_session.return_value,
|
|
helpers=[log_hlp.log_helper, vio_hlp.vios_busy_retry_helper])
|
|
|
|
mock_get_host_uuid.assert_called_once_with(mock_adapter.return_value)
|
|
mock_ensure_ltm.assert_called_once_with(mock_adapter.return_value,
|
|
'host_uuid')
|
|
mock_cache.assert_called_once_with(mock_adapter.return_value,
|
|
'host_uuid')
|
|
self.assertEqual(mock_cache.return_value, inspector.vm_metrics)
|
|
|
|
|
|
class TestPowerVMInspector(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestPowerVMInspector, self).setUp()
|
|
|
|
# These fixtures allow for stand up of the unit tests that use
|
|
# pypowervm.
|
|
pvm_adpt_fx = self.useFixture(api_fx.AdapterFx())
|
|
self.adpt = pvm_adpt_fx.adpt
|
|
pvm_mon_fx = self.useFixture(pvm_fixtures.PyPowerVMMetrics())
|
|
|
|
# Individual test cases will set return values on the metrics that
|
|
# come back from pypowervm.
|
|
self.mock_metrics = pvm_mon_fx.vm_metrics
|
|
|
|
with mock.patch('ceilometer_powervm.compute.virt.powervm.inspector.'
|
|
'PowerVMInspector._get_host_uuid'):
|
|
# Create the inspector
|
|
self.inspector = p_inspect.PowerVMInspector(None)
|
|
self.inspector.vm_metrics = self.mock_metrics
|
|
|
|
def test_inspect_instance_stats(self):
|
|
"""Validates PowerVM's inspect_instance method."""
|
|
# Validate that an error is raised if the instance can't be found
|
|
# in the sample
|
|
self.mock_metrics.get_latest_metric.return_value = None, None
|
|
self.assertRaises(virt_inspector.InstanceNotFoundException,
|
|
self.inspector.inspect_instance, mock.Mock(), None)
|
|
|
|
def mock_metric(util_cap, util_uncap, idle, donated, entitled):
|
|
"""Helper method to create mock proc metrics."""
|
|
metric = mock.MagicMock()
|
|
metric.processor.virt_procs = 12
|
|
metric.processor.util_cap_proc_cycles = util_cap
|
|
metric.processor.util_uncap_proc_cycles = util_uncap
|
|
metric.processor.idle_proc_cycles = idle
|
|
metric.processor.donated_proc_cycles = donated
|
|
metric.processor.entitled_proc_cycles = entitled
|
|
return metric
|
|
|
|
# Validate that the CPU metrics raise an issue if the previous metric
|
|
# can't be found (perhaps due to a live migration).
|
|
self.mock_metrics.get_latest_metric.return_value = (
|
|
mock.Mock(), mock_metric(7000, 50, 1000, 5000, 10000))
|
|
self.mock_metrics.get_previous_metric.return_value = None, None
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(7050, resp.cpu_time)
|
|
self.assertEqual(12, resp.cpu_number)
|
|
|
|
# Mock up a mixed use environment.
|
|
cur = mock_metric(7000, 50, 1000, 5000, 10000)
|
|
prev = mock_metric(4000, 25, 500, 2500, 5000)
|
|
self.mock_metrics.get_latest_metric.return_value = mock.Mock(), cur
|
|
self.mock_metrics.get_previous_metric.return_value = mock.Mock(), prev
|
|
|
|
# Execute and validate
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(.5, resp.cpu_util)
|
|
|
|
# Mock an instance with a dedicated processor, but idling and donating
|
|
# cycles to others. In these scenarios, util_cap shows full use, but
|
|
# the idle and donated get subtracted out.
|
|
cur = mock_metric(10000, 0, 100, 4900, 10000)
|
|
prev = mock_metric(5000, 0, 50, 2500, 5000)
|
|
self.mock_metrics.get_latest_metric.return_value = mock.Mock(), cur
|
|
self.mock_metrics.get_previous_metric.return_value = mock.Mock(), prev
|
|
|
|
# Execute and validate
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(51.0, resp.cpu_util)
|
|
|
|
# Mock an instance with a shared processor. By nature, this doesn't
|
|
# idle or donate. If it is 'idling' it is simply giving the cycles
|
|
# out. Show a low use one without needing extra cycles
|
|
cur = mock_metric(9000, 0, 0, 0, 10000)
|
|
prev = mock_metric(5000, 0, 0, 0, 5000)
|
|
self.mock_metrics.get_latest_metric.return_value = mock.Mock(), cur
|
|
self.mock_metrics.get_previous_metric.return_value = mock.Mock(), prev
|
|
|
|
# Execute and validate
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(80.0, resp.cpu_util)
|
|
|
|
# Mock an instance with a shared processor - but using cycles from
|
|
# the uncap pool. This means it is using extra cycles from other
|
|
# VMs that are currently not requiring the CPU.
|
|
cur = mock_metric(10000, 10000, 0, 0, 10000)
|
|
prev = mock_metric(5000, 0, 0, 0, 5000)
|
|
self.mock_metrics.get_latest_metric.return_value = mock.Mock(), cur
|
|
self.mock_metrics.get_previous_metric.return_value = mock.Mock(), prev
|
|
|
|
# Execute and validate. This should be running at 300% CPU
|
|
# utilization. Fast!
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(300.0, resp.cpu_util)
|
|
|
|
# Mock an instance that hasn't been started yet.
|
|
cur = mock_metric(0, 0, 0, 0, 0)
|
|
prev = mock_metric(0, 0, 0, 0, 0)
|
|
self.mock_metrics.get_latest_metric.return_value = mock.Mock(), cur
|
|
self.mock_metrics.get_previous_metric.return_value = mock.Mock(), prev
|
|
|
|
# This should have 0% utilization
|
|
resp = self.inspector.inspect_instance(mock.Mock(), None)
|
|
self.assertEqual(0.0, resp.cpu_util)
|
|
|
|
@staticmethod
|
|
def _mock_vnic_metric(rec_bytes, tx_bytes, rec_pkts, tx_pkts, phys_loc,
|
|
drop_pkts):
|
|
"""Helper method to create a specific mock network metric."""
|
|
return mock.Mock(received_bytes=rec_bytes, sent_bytes=tx_bytes,
|
|
received_packets=rec_pkts, sent_packets=tx_pkts,
|
|
dropped_packets=drop_pkts, physical_location=phys_loc)
|
|
|
|
def _build_cur_mock_vnic_metrics(self):
|
|
"""Helper method to create mock network metrics."""
|
|
cna1 = self._mock_vnic_metric(1000, 1000, 10, 10, 'a', 1)
|
|
cna2 = self._mock_vnic_metric(2000, 2000, 20, 20, 'b', 2)
|
|
cna3 = self._mock_vnic_metric(3000, 3000, 30, 30, 'c', 3)
|
|
|
|
metric = mock.MagicMock()
|
|
metric.network.cnas = [cna1, cna2, cna3]
|
|
return metric
|
|
|
|
def _build_prev_mock_vnic_metrics(self):
|
|
"""Helper method to create mock network metrics."""
|
|
cna1 = self._mock_vnic_metric(1000, 1000, 10, 10, 'a', 1)
|
|
cna2 = self._mock_vnic_metric(200, 200, 20, 20, 'b', 2)
|
|
|
|
metric = mock.MagicMock()
|
|
metric.network.cnas = [cna1, cna2]
|
|
return metric
|
|
|
|
@staticmethod
|
|
def _build_mock_cnas():
|
|
"""Builds a set of mock client network adapters."""
|
|
cna1 = mock.MagicMock()
|
|
cna1.loc_code, cna1.mac = 'a', 'AABBCCDDEEFF'
|
|
|
|
cna2 = mock.MagicMock()
|
|
cna2.loc_code, cna2.mac = 'b', 'AABBCCDDEE11'
|
|
|
|
cna3 = mock.MagicMock()
|
|
cna3.loc_code, cna3.mac = 'c', 'AABBCCDDEE22'
|
|
|
|
return [cna1, cna2, cna3]
|
|
|
|
@mock.patch('pypowervm.wrappers.network.CNA.wrap')
|
|
def test_inspect_vnics(self, mock_wrap):
|
|
"""Tests the inspect_vnics inspector method for PowerVM."""
|
|
# Validate that an error is raised if the instance can't be found in
|
|
# the sample data.
|
|
self.mock_metrics.get_latest_metric.return_value = None, None
|
|
self.assertRaises(virt_inspector.InstanceNotFoundException, list,
|
|
self.inspector.inspect_vnics(mock.Mock(), None))
|
|
|
|
# Validate that no data is returned if there is a current metric,
|
|
# just no network within it.
|
|
mock_empty_net = mock.MagicMock()
|
|
mock_empty_net.network = None
|
|
self.mock_metrics.get_latest_metric.return_value = None, mock_empty_net
|
|
self.assertEqual(
|
|
[], list(self.inspector.inspect_vnics(mock.Mock(), None)))
|
|
|
|
# Build a couple CNAs and verify we get the proper list back
|
|
mock_wrap.return_value = self._build_mock_cnas()
|
|
self.adpt.read.return_value = mock.Mock()
|
|
mock_metrics = self._build_cur_mock_vnic_metrics()
|
|
self.mock_metrics.get_latest_metric.return_value = None, mock_metrics
|
|
|
|
resp = list(self.inspector.inspect_vnics(mock.Mock(), None))
|
|
self.assertEqual(3, len(resp))
|
|
|
|
stats1 = resp[0]
|
|
self.assertEqual('aa:bb:cc:dd:ee:ff', stats1.mac)
|
|
self.assertEqual('a', stats1.name)
|
|
self.assertEqual(1000, stats1.rx_bytes)
|
|
self.assertEqual(1000, stats1.tx_bytes)
|
|
self.assertEqual(10, stats1.rx_packets)
|
|
self.assertEqual(10, stats1.tx_packets)
|
|
self.assertEqual(1, stats1.rx_drop)
|
|
self.assertEqual(0, stats1.tx_drop)
|
|
self.assertEqual(0, stats1.rx_errors)
|
|
self.assertEqual(0, stats1.tx_errors)
|
|
|
|
@mock.patch('pypowervm.wrappers.network.CNA.wrap')
|
|
def test_inspect_vnic_rates(self, mock_wrap):
|
|
"""Tests the inspect_vnic_rates inspector method for PowerVM."""
|
|
# Validate that an error is raised if the instance can't be found in
|
|
# the sample data.
|
|
self.mock_metrics.get_latest_metric.return_value = None, None
|
|
self.mock_metrics.get_previous_metric.return_value = None, None
|
|
self.assertRaises(virt_inspector.InstanceNotFoundException, list,
|
|
self.inspector.inspect_vnic_rates(mock.Mock(), None))
|
|
|
|
# Validate that no data is returned if there is a current metric,
|
|
# just no network within it.
|
|
mock_empty_net = mock.MagicMock()
|
|
mock_empty_net.network = None
|
|
self.mock_metrics.get_latest_metric.return_value = None, mock_empty_net
|
|
self.assertEqual(
|
|
[], list(self.inspector.inspect_vnic_rates(mock.Mock(), None)))
|
|
|
|
# Build the response LPAR data
|
|
mock_wrap.return_value = self._build_mock_cnas()
|
|
self.adpt.read.return_value = mock.Mock()
|
|
|
|
# Current metric data
|
|
mock_cur = self._build_cur_mock_vnic_metrics()
|
|
cur_date = datetime.datetime.now()
|
|
self.mock_metrics.get_latest_metric.return_value = cur_date, mock_cur
|
|
|
|
# Build the previous
|
|
mock_prev = self._build_prev_mock_vnic_metrics()
|
|
prev_date = cur_date - datetime.timedelta(seconds=30)
|
|
self.mock_metrics.get_previous_metric.return_value = (prev_date,
|
|
mock_prev)
|
|
|
|
# Execute
|
|
resp = list(self.inspector.inspect_vnic_rates(mock.Mock(), None))
|
|
self.assertEqual(3, len(resp))
|
|
|
|
# First metric. No delta
|
|
stats1 = resp[0]
|
|
self.assertEqual('aa:bb:cc:dd:ee:ff', stats1.mac)
|
|
self.assertEqual('a', stats1.name)
|
|
self.assertEqual(0, stats1.rx_bytes_rate)
|
|
self.assertEqual(0, stats1.tx_bytes_rate)
|
|
|
|
# Second metric
|
|
stats2 = resp[1]
|
|
self.assertEqual('aa:bb:cc:dd:ee:11', stats2.mac)
|
|
self.assertEqual('b', stats2.name)
|
|
self.assertEqual(60.0, stats2.rx_bytes_rate)
|
|
self.assertEqual(60.0, stats2.tx_bytes_rate)
|
|
|
|
# Third metric had no previous.
|
|
stats3 = resp[2]
|
|
self.assertEqual('aa:bb:cc:dd:ee:22', stats3.mac)
|
|
self.assertEqual('c', stats3.name)
|
|
self.assertEqual(100.0, stats3.rx_bytes_rate)
|
|
self.assertEqual(100.0, stats3.tx_bytes_rate)
|
|
|
|
@staticmethod
|
|
def _mock_stor_metric(num_reads, num_writes, read_bytes, write_bytes,
|
|
name):
|
|
"""Helper method to create a specific mock storage metric."""
|
|
m = mock.Mock(num_reads=num_reads, num_writes=num_writes,
|
|
read_bytes=read_bytes, write_bytes=write_bytes)
|
|
# Have to do this method as name is a special field.
|
|
m.configure_mock(name=name)
|
|
return m
|
|
|
|
def _build_cur_mock_stor_metrics(self):
|
|
"""Helper method to create mock storage metrics."""
|
|
vscsi1 = self._mock_stor_metric(1000, 1000, 100000, 100000, 'vscsi1')
|
|
vscsi2 = self._mock_stor_metric(2000, 2000, 200000, 200000, 'vscsi2')
|
|
vfc1 = self._mock_stor_metric(3000, 3000, 300000, 300000, 'vfc1')
|
|
|
|
storage = mock.Mock(virt_adpts=[vscsi1, vscsi2], vfc_adpts=[vfc1])
|
|
metric = mock.MagicMock(storage=storage)
|
|
return metric
|
|
|
|
def _build_prev_mock_stor_metrics(self):
|
|
"""Helper method to create mock storage metrics."""
|
|
vscsi1 = self._mock_stor_metric(500, 500, 50000, 50000, 'vscsi1')
|
|
vfc1 = self._mock_stor_metric(2000, 2000, 20000, 200000, 'vfc1')
|
|
|
|
storage = mock.Mock(virt_adpts=[vscsi1], vfc_adpts=[vfc1])
|
|
metric = mock.MagicMock(storage=storage)
|
|
return metric
|
|
|
|
def test_inspect_disk_iops(self):
|
|
"""Tests the inspect_disk_iops inspector method for PowerVM."""
|
|
# Validate that an error is raised if the instance can't be found in
|
|
# the sample data.
|
|
self.mock_metrics.get_latest_metric.return_value = None, None
|
|
self.mock_metrics.get_previous_metric.return_value = None, None
|
|
self.assertRaises(virt_inspector.InstanceNotFoundException, list,
|
|
self.inspector.inspect_disk_iops(mock.Mock(), None))
|
|
|
|
# Validate that no data is returned if there is a current metric,
|
|
# just no storage within it.
|
|
mock_empty_st = mock.MagicMock(storage=None)
|
|
self.mock_metrics.get_latest_metric.return_value = None, mock_empty_st
|
|
self.assertEqual(
|
|
[], list(self.inspector.inspect_disk_iops(mock.Mock(), None)))
|
|
|
|
# Current metric data
|
|
mock_cur = self._build_cur_mock_stor_metrics()
|
|
cur_date = datetime.datetime.now()
|
|
self.mock_metrics.get_latest_metric.return_value = cur_date, mock_cur
|
|
|
|
# Validate that if there is no previous data, get no data back.
|
|
self.assertEqual(
|
|
[], list(self.inspector.inspect_disk_iops(mock.Mock(), None)))
|
|
|
|
# Build the previous
|
|
mock_prev = self._build_prev_mock_stor_metrics()
|
|
prev_date = cur_date - datetime.timedelta(seconds=30)
|
|
self.mock_metrics.get_previous_metric.return_value = (prev_date,
|
|
mock_prev)
|
|
|
|
# Execute
|
|
resp = list(self.inspector.inspect_disk_iops(mock.Mock(), None))
|
|
self.assertEqual(3, len(resp))
|
|
|
|
# Two vSCSI's
|
|
stats1 = resp[0]
|
|
self.assertEqual('vscsi1', stats1.device)
|
|
self.assertEqual(33, stats1.iops_count)
|
|
|
|
stats2 = resp[1]
|
|
self.assertEqual('vscsi2', stats2.device)
|
|
self.assertEqual(133, stats2.iops_count)
|
|
|
|
# Next is the vFC metric
|
|
stats3 = resp[2]
|
|
self.assertEqual('vfc1', stats3.device)
|
|
self.assertEqual(66, stats3.iops_count)
|
|
|
|
def test_inspect_disks(self):
|
|
"""Tests the inspect_disks inspector method for PowerVM."""
|
|
# Validate that an error is raised if the instance can't be found in
|
|
# the sample data.
|
|
self.mock_metrics.get_latest_metric.return_value = None, None
|
|
self.assertRaises(virt_inspector.InstanceNotFoundException, list,
|
|
self.inspector.inspect_disks(mock.Mock(), None))
|
|
|
|
# Validate that no data is returned if there is a current metric,
|
|
# just no storage within it.
|
|
mock_empty_st = mock.MagicMock(storage=None)
|
|
self.mock_metrics.get_latest_metric.return_value = None, mock_empty_st
|
|
self.assertEqual([],
|
|
list(self.inspector.inspect_disks(mock.Mock(), None)))
|
|
|
|
# Current metric data
|
|
mock_cur = self._build_cur_mock_stor_metrics()
|
|
cur_date = datetime.datetime.now()
|
|
self.mock_metrics.get_latest_metric.return_value = cur_date, mock_cur
|
|
|
|
# Execute
|
|
resp = list(self.inspector.inspect_disks(mock.Mock(), None))
|
|
self.assertEqual(3, len(resp))
|
|
|
|
# Two vSCSIs.
|
|
stats1 = resp[0]
|
|
self.assertEqual('vscsi1', stats1.device)
|
|
self.assertEqual(1000, stats1.read_requests)
|
|
self.assertEqual(100000, stats1.read_bytes)
|
|
self.assertEqual(1000, stats1.write_requests)
|
|
self.assertEqual(100000, stats1.write_bytes)
|
|
self.assertEqual(0, stats1.errors)
|
|
self.assertEqual(0, stats1.wr_total_times)
|
|
self.assertEqual(0, stats1.rd_total_times)
|
|
|
|
stats2 = resp[1]
|
|
self.assertEqual('vscsi2', stats2.device)
|
|
self.assertEqual(2000, stats2.read_requests)
|
|
self.assertEqual(200000, stats2.read_bytes)
|
|
self.assertEqual(2000, stats2.write_requests)
|
|
self.assertEqual(200000, stats2.write_bytes)
|
|
self.assertEqual(0, stats2.errors)
|
|
self.assertEqual(0, stats2.wr_total_times)
|
|
self.assertEqual(0, stats2.rd_total_times)
|
|
|
|
# Next is the vFC metric
|
|
stats3 = resp[2]
|
|
self.assertEqual('vfc1', stats3.device)
|
|
self.assertEqual(3000, stats3.read_requests)
|
|
self.assertEqual(300000, stats3.read_bytes)
|
|
self.assertEqual(3000, stats3.write_requests)
|
|
self.assertEqual(300000, stats3.write_bytes)
|
|
self.assertEqual(0, stats3.errors)
|
|
self.assertEqual(0, stats3.wr_total_times)
|
|
self.assertEqual(0, stats3.rd_total_times)
|