Replace CRLF by LF

We generally use LF as newline code.

Change-Id: I6d5d2ee6dfd159df025a3446d4567f9972faa06e
This commit is contained in:
Takashi Kajinami 2024-01-27 12:24:58 +09:00
parent 2a18843ab9
commit 53d8dba3f7
11 changed files with 813 additions and 813 deletions

@ -1,380 +1,380 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries. # Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
# a copy of the License at # a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from copy import deepcopy from copy import deepcopy
from unittest import mock from unittest import mock
from cinder.tests.unit import test from cinder.tests.unit import test
from cinder.tests.unit.volume.drivers.dell_emc.powermax import ( from cinder.tests.unit.volume.drivers.dell_emc.powermax import (
powermax_data as tpd) powermax_data as tpd)
from cinder.tests.unit.volume.drivers.dell_emc.powermax import ( from cinder.tests.unit.volume.drivers.dell_emc.powermax import (
powermax_fake_objects as tpfo) powermax_fake_objects as tpfo)
from cinder.volume.drivers.dell_emc.powermax import iscsi from cinder.volume.drivers.dell_emc.powermax import iscsi
from cinder.volume.drivers.dell_emc.powermax import performance from cinder.volume.drivers.dell_emc.powermax import performance
from cinder.volume.drivers.dell_emc.powermax import rest from cinder.volume.drivers.dell_emc.powermax import rest
from cinder.volume.drivers.dell_emc.powermax import utils from cinder.volume.drivers.dell_emc.powermax import utils
from cinder.volume import volume_utils from cinder.volume import volume_utils
class PowerMaxPerformanceTest(test.TestCase): class PowerMaxPerformanceTest(test.TestCase):
def setUp(self): def setUp(self):
self.data = tpd.PowerMaxData() self.data = tpd.PowerMaxData()
self.reference_cinder_conf = tpfo.FakeConfiguration( self.reference_cinder_conf = tpfo.FakeConfiguration(
None, 'ProvisionTests', 1, 1, san_ip='1.1.1.1', san_login='smc', None, 'ProvisionTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
powermax_array=self.data.array, powermax_srp='SRP_1', powermax_array=self.data.array, powermax_srp='SRP_1',
san_password='smc', san_api_port=8443, san_password='smc', san_api_port=8443,
powermax_port_groups=[self.data.port_group_name_i], powermax_port_groups=[self.data.port_group_name_i],
load_balance=True, load_balance_real_time=True, load_balance=True, load_balance_real_time=True,
load_data_format='avg', load_look_back=60, load_data_format='avg', load_look_back=60,
load_look_back_real_time=10, port_group_load_metric='PercentBusy', load_look_back_real_time=10, port_group_load_metric='PercentBusy',
port_load_metric='PercentBusy') port_load_metric='PercentBusy')
self.reference_perf_conf = { self.reference_perf_conf = {
'load_balance': True, 'load_balance_rt': True, 'load_balance': True, 'load_balance_rt': True,
'perf_registered': True, 'rt_registered': True, 'perf_registered': True, 'rt_registered': True,
'collection_interval': 5, 'data_format': 'Average', 'collection_interval': 5, 'data_format': 'Average',
'look_back': 60, 'look_back_rt': 10, 'look_back': 60, 'look_back_rt': 10,
'port_group_metric': 'PercentBusy', 'port_metric': 'PercentBusy'} 'port_group_metric': 'PercentBusy', 'port_metric': 'PercentBusy'}
super(PowerMaxPerformanceTest, self).setUp() super(PowerMaxPerformanceTest, self).setUp()
self.mock_object(volume_utils, 'get_max_over_subscription_ratio') self.mock_object(volume_utils, 'get_max_over_subscription_ratio')
self.mock_object(rest.PowerMaxRest, '_establish_rest_session', self.mock_object(rest.PowerMaxRest, '_establish_rest_session',
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
driver = iscsi.PowerMaxISCSIDriver( driver = iscsi.PowerMaxISCSIDriver(
configuration=self.reference_cinder_conf) configuration=self.reference_cinder_conf)
self.driver = driver self.driver = driver
self.common = self.driver.common self.common = self.driver.common
self.performance = self.driver.performance self.performance = self.driver.performance
self.rest = self.common.rest self.rest = self.common.rest
def test_set_performance_configuration(self): def test_set_performance_configuration(self):
"""Test set_performance_configuration diagnostic & real time.""" """Test set_performance_configuration diagnostic & real time."""
self.assertEqual(self.reference_perf_conf, self.performance.config) self.assertEqual(self.reference_perf_conf, self.performance.config)
@mock.patch.object( @mock.patch.object(
performance.PowerMaxPerformance, 'get_array_registration_details', performance.PowerMaxPerformance, 'get_array_registration_details',
return_value=(True, False, 5)) return_value=(True, False, 5))
def test_set_performance_configuration_no_rt_reg_rt_disabled( def test_set_performance_configuration_no_rt_reg_rt_disabled(
self, mck_reg): self, mck_reg):
"""Test set_performance_configuration real-time disabled. """Test set_performance_configuration real-time disabled.
Test configurations settings when real-time is disabled in cinder.conf Test configurations settings when real-time is disabled in cinder.conf
and real-time metrics are not registered in Unisphere. and real-time metrics are not registered in Unisphere.
""" """
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_balance_real_time = False cinder_conf.load_balance_real_time = False
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = deepcopy(self.reference_perf_conf) perf_conf = deepcopy(self.reference_perf_conf)
perf_conf['load_balance_rt'] = False perf_conf['load_balance_rt'] = False
perf_conf['rt_registered'] = False perf_conf['rt_registered'] = False
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
def test_set_performance_configuration_rt_reg_rt_disabled(self): def test_set_performance_configuration_rt_reg_rt_disabled(self):
"""Test set_performance_configuration real-time disabled v2. """Test set_performance_configuration real-time disabled v2.
Test configurations settings when real-time is disabled in cinder.conf Test configurations settings when real-time is disabled in cinder.conf
and real-time metrics are registered in Unisphere. and real-time metrics are registered in Unisphere.
""" """
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_balance_real_time = False cinder_conf.load_balance_real_time = False
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = deepcopy(self.reference_perf_conf) perf_conf = deepcopy(self.reference_perf_conf)
perf_conf['load_balance_rt'] = False perf_conf['load_balance_rt'] = False
perf_conf['rt_registered'] = True perf_conf['rt_registered'] = True
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
@mock.patch.object( @mock.patch.object(
performance.PowerMaxPerformance, 'get_array_registration_details', performance.PowerMaxPerformance, 'get_array_registration_details',
return_value=(False, False, 5)) return_value=(False, False, 5))
def test_set_performance_configuration_not_perf_registered(self, mck_reg): def test_set_performance_configuration_not_perf_registered(self, mck_reg):
"""Test set_performance_configuration performance metrics not enabled. """Test set_performance_configuration performance metrics not enabled.
This tests config settings where user has enabled load balancing in This tests config settings where user has enabled load balancing in
cinder.conf but Unisphere is not registered for performance metrics. cinder.conf but Unisphere is not registered for performance metrics.
""" """
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = {'load_balance': False} perf_conf = {'load_balance': False}
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
def test_set_performance_configuration_invalid_data_format(self): def test_set_performance_configuration_invalid_data_format(self):
"""Test set_performance_configuration invalid data format, avg set.""" """Test set_performance_configuration invalid data format, avg set."""
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_data_format = 'InvalidFormat' cinder_conf.load_data_format = 'InvalidFormat'
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
self.assertEqual(self.reference_perf_conf, self.assertEqual(self.reference_perf_conf,
temp_driver.performance.config) temp_driver.performance.config)
def test_set_performance_configuration_max_data_format(self): def test_set_performance_configuration_max_data_format(self):
"""Test set_performance_configuration max data format, max set.""" """Test set_performance_configuration max data format, max set."""
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_data_format = 'MAXIMUM' cinder_conf.load_data_format = 'MAXIMUM'
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = deepcopy(self.reference_perf_conf) perf_conf = deepcopy(self.reference_perf_conf)
perf_conf['data_format'] = 'Maximum' perf_conf['data_format'] = 'Maximum'
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
def test_set_performance_configuration_lookback_invalid(self): def test_set_performance_configuration_lookback_invalid(self):
"""Test set_performance_configuration invalid lookback windows.""" """Test set_performance_configuration invalid lookback windows."""
# Window set to negative value # Window set to negative value
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_look_back = -1 cinder_conf.load_look_back = -1
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = deepcopy(self.reference_perf_conf) perf_conf = deepcopy(self.reference_perf_conf)
perf_conf['look_back'] = 60 perf_conf['look_back'] = 60
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
# Window set to value larger than upper limit of 1440 # Window set to value larger than upper limit of 1440
cinder_conf.load_look_back = 9999 cinder_conf.load_look_back = 9999
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
def test_set_performance_configuration_rt_lookback_invalid(self): def test_set_performance_configuration_rt_lookback_invalid(self):
"""Test set_performance_configuration invalid rt lookback windows.""" """Test set_performance_configuration invalid rt lookback windows."""
# Window set to negative value # Window set to negative value
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.load_look_back_real_time = -1 cinder_conf.load_look_back_real_time = -1
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
perf_conf = deepcopy(self.reference_perf_conf) perf_conf = deepcopy(self.reference_perf_conf)
perf_conf['look_back_rt'] = 1 perf_conf['look_back_rt'] = 1
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
# Window set to value larger than upper limit of 1440 # Window set to value larger than upper limit of 1440
cinder_conf.load_look_back_real_time = 100 cinder_conf.load_look_back_real_time = 100
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
self.assertEqual(perf_conf, temp_driver.performance.config) self.assertEqual(perf_conf, temp_driver.performance.config)
def test_set_performance_configuration_invalid_pg_metric(self): def test_set_performance_configuration_invalid_pg_metric(self):
"""Test set_performance_configuration invalid pg metric.""" """Test set_performance_configuration invalid pg metric."""
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.port_group_load_metric = 'InvalidMetric' cinder_conf.port_group_load_metric = 'InvalidMetric'
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
self.assertEqual(self.reference_perf_conf, self.assertEqual(self.reference_perf_conf,
temp_driver.performance.config) temp_driver.performance.config)
def test_set_performance_configuration_invalid_port_metric(self): def test_set_performance_configuration_invalid_port_metric(self):
"""Test set_performance_configuration invalid port metric.""" """Test set_performance_configuration invalid port metric."""
cinder_conf = deepcopy(self.reference_cinder_conf) cinder_conf = deepcopy(self.reference_cinder_conf)
cinder_conf.port_load_metric = 'InvalidMetric' cinder_conf.port_load_metric = 'InvalidMetric'
rest.PowerMaxRest._establish_rest_session = mock.Mock( rest.PowerMaxRest._establish_rest_session = mock.Mock(
return_value=tpfo.FakeRequestsSession()) return_value=tpfo.FakeRequestsSession())
temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf) temp_driver = iscsi.PowerMaxISCSIDriver(configuration=cinder_conf)
self.assertEqual(self.reference_perf_conf, self.assertEqual(self.reference_perf_conf,
temp_driver.performance.config) temp_driver.performance.config)
def test_get_array_registration_details(self): def test_get_array_registration_details(self):
"""Test get_array_registration_details.""" """Test get_array_registration_details."""
p_reg, rt_reg, c_int = self.performance.get_array_registration_details( p_reg, rt_reg, c_int = self.performance.get_array_registration_details(
self.data.array) self.data.array)
self.assertEqual((True, True, 5), (p_reg, rt_reg, c_int)) self.assertEqual((True, True, 5), (p_reg, rt_reg, c_int))
def test_get_array_performance_keys(self): def test_get_array_performance_keys(self):
"""Test get_array_performance_keys.""" """Test get_array_performance_keys."""
f_date, l_date = self.performance.get_array_performance_keys( f_date, l_date = self.performance.get_array_performance_keys(
self.data.array) self.data.array)
self.assertEqual(self.data.f_date_a, f_date) self.assertEqual(self.data.f_date_a, f_date)
self.assertEqual(self.data.l_date, l_date) self.assertEqual(self.data.l_date, l_date)
def test_get_look_back_window_interval_timestamp(self): def test_get_look_back_window_interval_timestamp(self):
"""Test _get_look_back_window_interval_timestamp.""" """Test _get_look_back_window_interval_timestamp."""
self.assertEqual( self.assertEqual(
self.data.l_date - (utils.ONE_MINUTE * 10), self.data.l_date - (utils.ONE_MINUTE * 10),
self.performance._get_look_back_window_interval_timestamp( self.performance._get_look_back_window_interval_timestamp(
self.data.l_date, 10)) self.data.l_date, 10))
def test_process_load(self): def test_process_load(self):
"""Test _process_load to calculate average of all intervals.""" """Test _process_load to calculate average of all intervals."""
performance_data = self.data.dummy_performance_data performance_data = self.data.dummy_performance_data
perf_metrics = performance_data['resultList']['result'] perf_metrics = performance_data['resultList']['result']
metric = self.data.perf_pb_metric metric = self.data.perf_pb_metric
ref_total = 0 ref_total = 0
for interval in perf_metrics: for interval in perf_metrics:
ref_total += interval.get(metric) ref_total += interval.get(metric)
ref_avg = ref_total / len(perf_metrics) ref_avg = ref_total / len(perf_metrics)
avg, total, count = self.performance._process_load( avg, total, count = self.performance._process_load(
performance_data, metric) performance_data, metric)
self.assertEqual(avg, ref_avg) self.assertEqual(avg, ref_avg)
self.assertEqual(total, ref_total) self.assertEqual(total, ref_total)
self.assertEqual(count, len(perf_metrics)) self.assertEqual(count, len(perf_metrics))
def test_get_port_group_performance_stats(self): def test_get_port_group_performance_stats(self):
"""Test _get_port_group_performance_stats.""" """Test _get_port_group_performance_stats."""
array_id = self.data.array array_id = self.data.array
port_group_id = self.data.port_group_name_i port_group_id = self.data.port_group_name_i
f_date = self.data.f_date_a f_date = self.data.f_date_a
l_date = self.data.l_date l_date = self.data.l_date
metric = self.data.perf_pb_metric metric = self.data.perf_pb_metric
data_format = self.data.perf_df_avg data_format = self.data.perf_df_avg
avg, total, count = self.performance._get_port_group_performance_stats( avg, total, count = self.performance._get_port_group_performance_stats(
array_id, port_group_id, f_date, l_date, metric, data_format) array_id, port_group_id, f_date, l_date, metric, data_format)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertTrue(total > 0) self.assertTrue(total > 0)
self.assertIsInstance(total, float) self.assertIsInstance(total, float)
self.assertTrue(count > 0) self.assertTrue(count > 0)
self.assertIsInstance(count, int) self.assertIsInstance(count, int)
def test_get_port_performance_stats_diagnostic(self): def test_get_port_performance_stats_diagnostic(self):
"""Test _get_port_performance_stats diagnostic.""" """Test _get_port_performance_stats diagnostic."""
array_id = self.data.array array_id = self.data.array
dir_id = self.data.iscsi_dir dir_id = self.data.iscsi_dir
port_id = self.data.iscsi_port port_id = self.data.iscsi_port
f_date = self.data.f_date_a f_date = self.data.f_date_a
l_date = self.data.l_date l_date = self.data.l_date
metric = self.data.perf_pb_metric metric = self.data.perf_pb_metric
data_format = self.data.perf_df_avg data_format = self.data.perf_df_avg
res_type = 'diagnostic' res_type = 'diagnostic'
ref_target_uri = '/performance/FEPort/metrics' ref_target_uri = '/performance/FEPort/metrics'
ref_resource = '%(res)s Port performance metrics' % {'res': res_type} ref_resource = '%(res)s Port performance metrics' % {'res': res_type}
ref_request_body = { ref_request_body = {
utils.SYMM_ID: array_id, utils.DIR_ID: dir_id, utils.SYMM_ID: array_id, utils.DIR_ID: dir_id,
utils.PORT_ID: port_id, utils.S_DATE: f_date, utils.E_DATE: l_date, utils.PORT_ID: port_id, utils.S_DATE: f_date, utils.E_DATE: l_date,
utils.DATA_FORMAT: data_format, utils.METRICS: [metric]} utils.DATA_FORMAT: data_format, utils.METRICS: [metric]}
with mock.patch.object( with mock.patch.object(
self.rest, 'post_request', self.rest, 'post_request',
side_effect=self.rest.post_request) as mck_post: side_effect=self.rest.post_request) as mck_post:
avg, total, count = self.performance._get_port_performance_stats( avg, total, count = self.performance._get_port_performance_stats(
array_id, dir_id, port_id, f_date, l_date, metric, data_format, array_id, dir_id, port_id, f_date, l_date, metric, data_format,
real_time=False) real_time=False)
mck_post.assert_called_once_with( mck_post.assert_called_once_with(
ref_target_uri, ref_resource, ref_request_body) ref_target_uri, ref_resource, ref_request_body)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertTrue(total > 0) self.assertTrue(total > 0)
self.assertIsInstance(total, float) self.assertIsInstance(total, float)
self.assertTrue(count > 0) self.assertTrue(count > 0)
self.assertIsInstance(count, int) self.assertIsInstance(count, int)
def test_get_port_performance_stats_real_time(self): def test_get_port_performance_stats_real_time(self):
"""Test _get_port_performance_stats real-time.""" """Test _get_port_performance_stats real-time."""
array_id = self.data.array array_id = self.data.array
dir_id = self.data.iscsi_dir dir_id = self.data.iscsi_dir
port_id = self.data.iscsi_port port_id = self.data.iscsi_port
f_date = self.data.f_date_a f_date = self.data.f_date_a
l_date = self.data.l_date l_date = self.data.l_date
metric = self.data.perf_pb_metric metric = self.data.perf_pb_metric
res_type = 'real-time' res_type = 'real-time'
ref_target_uri = '/performance/realtime/metrics' ref_target_uri = '/performance/realtime/metrics'
ref_resource = '%(res)s Port performance metrics' % {'res': res_type} ref_resource = '%(res)s Port performance metrics' % {'res': res_type}
ref_request_body = { ref_request_body = {
utils.SYMM_ID: array_id, utils.SYMM_ID: array_id,
utils.INST_ID: self.data.iscsi_dir_port, utils.INST_ID: self.data.iscsi_dir_port,
utils.S_DATE: f_date, utils.E_DATE: l_date, utils.S_DATE: f_date, utils.E_DATE: l_date,
utils.CAT: utils.FE_PORT_RT, utils.METRICS: [metric]} utils.CAT: utils.FE_PORT_RT, utils.METRICS: [metric]}
with mock.patch.object( with mock.patch.object(
self.rest, 'post_request', self.rest, 'post_request',
side_effect=self.rest.post_request) as mck_post: side_effect=self.rest.post_request) as mck_post:
avg, total, count = self.performance._get_port_performance_stats( avg, total, count = self.performance._get_port_performance_stats(
array_id, dir_id, port_id, f_date, l_date, metric, array_id, dir_id, port_id, f_date, l_date, metric,
real_time=True) real_time=True)
mck_post.assert_called_once_with( mck_post.assert_called_once_with(
ref_target_uri, ref_resource, ref_request_body) ref_target_uri, ref_resource, ref_request_body)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertTrue(total > 0) self.assertTrue(total > 0)
self.assertIsInstance(total, float) self.assertIsInstance(total, float)
self.assertTrue(count > 0) self.assertTrue(count > 0)
self.assertIsInstance(count, int) self.assertIsInstance(count, int)
def test_process_port_group_load_min(self): def test_process_port_group_load_min(self):
"""Test process_port_group_load min load.""" """Test process_port_group_load min load."""
array_id = self.data.array array_id = self.data.array
port_groups = self.data.perf_port_groups port_groups = self.data.perf_port_groups
avg, metric, port_group = self.performance.process_port_group_load( avg, metric, port_group = self.performance.process_port_group_load(
array_id, port_groups) array_id, port_groups)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port_group, port_groups) self.assertIn(port_group, port_groups)
def test_process_port_group_load_max(self): def test_process_port_group_load_max(self):
"""Test process_port_group_load max load.""" """Test process_port_group_load max load."""
array_id = self.data.array array_id = self.data.array
port_groups = self.data.perf_port_groups port_groups = self.data.perf_port_groups
avg, metric, port_group = self.performance.process_port_group_load( avg, metric, port_group = self.performance.process_port_group_load(
array_id, port_groups, max_load=True) array_id, port_groups, max_load=True)
self.assertTrue(abs(avg) > 0) self.assertTrue(abs(avg) > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port_group, port_groups) self.assertIn(port_group, port_groups)
def test_process_port_load_real_time_min(self): def test_process_port_load_real_time_min(self):
"""Test process_port_load min load real-time.""" """Test process_port_load min load real-time."""
array_id = self.data.array array_id = self.data.array
ports = self.data.perf_ports ports = self.data.perf_ports
avg, metric, port = self.performance.process_port_group_load( avg, metric, port = self.performance.process_port_group_load(
array_id, ports) array_id, ports)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port, ports) self.assertIn(port, ports)
def test_process_port_load_real_time_max(self): def test_process_port_load_real_time_max(self):
"""Test process_port_load max load real-time.""" """Test process_port_load max load real-time."""
array_id = self.data.array array_id = self.data.array
ports = self.data.perf_ports ports = self.data.perf_ports
avg, metric, port = self.performance.process_port_group_load( avg, metric, port = self.performance.process_port_group_load(
array_id, ports, max_load=True) array_id, ports, max_load=True)
self.assertTrue(abs(avg) > 0) self.assertTrue(abs(avg) > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port, ports) self.assertIn(port, ports)
def test_process_port_load_diagnostic_min(self): def test_process_port_load_diagnostic_min(self):
"""Test process_port_load min load real-time.""" """Test process_port_load min load real-time."""
array_id = self.data.array array_id = self.data.array
ports = self.data.perf_ports ports = self.data.perf_ports
self.performance.config['load_balance_rt'] = False self.performance.config['load_balance_rt'] = False
avg, metric, port = self.performance.process_port_group_load( avg, metric, port = self.performance.process_port_group_load(
array_id, ports) array_id, ports)
self.assertTrue(avg > 0) self.assertTrue(avg > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port, ports) self.assertIn(port, ports)
def test_process_port_load_diagnostic_max(self): def test_process_port_load_diagnostic_max(self):
"""Test process_port_load min load real-time.""" """Test process_port_load min load real-time."""
array_id = self.data.array array_id = self.data.array
ports = self.data.perf_ports ports = self.data.perf_ports
self.performance.config['load_balance_rt'] = False self.performance.config['load_balance_rt'] = False
avg, metric, port = self.performance.process_port_group_load( avg, metric, port = self.performance.process_port_group_load(
array_id, ports, max_load=True) array_id, ports, max_load=True)
self.assertTrue(abs(avg) > 0) self.assertTrue(abs(avg) > 0)
self.assertIsInstance(avg, float) self.assertIsInstance(avg, float)
self.assertEqual(metric, self.assertEqual(metric,
self.performance.config.get('port_group_metric')) self.performance.config.get('port_group_metric'))
self.assertIn(port, ports) self.assertIn(port, ports)

@ -1,394 +1,394 @@
# Copyright (c) 2020 Dell Inc. or its subsidiaries. # Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
# a copy of the License at # a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from heapq import heappop from heapq import heappop
from heapq import heappush from heapq import heappush
import time import time
from oslo_log import log as logging from oslo_log import log as logging
from cinder.volume.drivers.dell_emc.powermax import utils from cinder.volume.drivers.dell_emc.powermax import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class PowerMaxPerformance(object): class PowerMaxPerformance(object):
"""Performance Class for Dell EMC PowerMax volume drivers. """Performance Class for Dell EMC PowerMax volume drivers.
It supports VMAX 3, All Flash and PowerMax arrays. It supports VMAX 3, All Flash and PowerMax arrays.
""" """
def __init__(self, rest, performance_config): def __init__(self, rest, performance_config):
self.rest = rest self.rest = rest
self.config = performance_config self.config = performance_config
def set_performance_configuration(self, array_id, cinder_conf): def set_performance_configuration(self, array_id, cinder_conf):
"""Set the performance configuration if details present in cinder.conf. """Set the performance configuration if details present in cinder.conf.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:param cinder_conf: cinder configuration options -- dict :param cinder_conf: cinder configuration options -- dict
""" """
# Get performance registration, real-time registration, and collection # Get performance registration, real-time registration, and collection
# interval information for PowerMax array # interval information for PowerMax array
p_reg, rt_reg, c_int = self.get_array_registration_details(array_id) p_reg, rt_reg, c_int = self.get_array_registration_details(array_id)
# Get load balance settings from cinder backend configuration # Get load balance settings from cinder backend configuration
lb_enabled = cinder_conf.safe_get(utils.LOAD_BALANCE) lb_enabled = cinder_conf.safe_get(utils.LOAD_BALANCE)
rt_enabled = cinder_conf.safe_get(utils.LOAD_BALANCE_RT) rt_enabled = cinder_conf.safe_get(utils.LOAD_BALANCE_RT)
# Real-time # Real-time
if rt_enabled and not rt_reg: if rt_enabled and not rt_reg:
LOG.warning( LOG.warning(
"Real-time load balancing is enabled but array %(arr)s is not " "Real-time load balancing is enabled but array %(arr)s is not "
"registered for real-time performance metrics collection. " "registered for real-time performance metrics collection. "
"Diagnostic performance metrics will be used instead.", "Diagnostic performance metrics will be used instead.",
{'arr': array_id}) {'arr': array_id})
rt_enabled = False rt_enabled = False
# Load balancing enabled but array not registered for perf metrics # Load balancing enabled but array not registered for perf metrics
if (lb_enabled or rt_enabled) and not p_reg: if (lb_enabled or rt_enabled) and not p_reg:
LOG.warning( LOG.warning(
"Load balancing is enabled but array %(arr)s is not " "Load balancing is enabled but array %(arr)s is not "
"registered for performance metrics collection. Reverting to " "registered for performance metrics collection. Reverting to "
"default random Port and Port Group selection", "default random Port and Port Group selection",
{'arr': array_id}) {'arr': array_id})
return {'load_balance': False} return {'load_balance': False}
data_format = cinder_conf.safe_get(utils.PERF_DATA_FORMAT) data_format = cinder_conf.safe_get(utils.PERF_DATA_FORMAT)
if data_format.lower() not in ['average', 'avg', 'maximum', 'max']: if data_format.lower() not in ['average', 'avg', 'maximum', 'max']:
LOG.warning("Incorrect data format '%(df)s', reverting to " LOG.warning("Incorrect data format '%(df)s', reverting to "
"default value 'Average'.", {'df': data_format}) "default value 'Average'.", {'df': data_format})
data_format = 'Average' data_format = 'Average'
if data_format.lower() in ['average', 'avg']: if data_format.lower() in ['average', 'avg']:
data_format = 'Average' data_format = 'Average'
elif data_format.lower() in ['maximum', 'max']: elif data_format.lower() in ['maximum', 'max']:
data_format = 'Maximum' data_format = 'Maximum'
# Get diagnostic metrics look back window # Get diagnostic metrics look back window
lb_diagnostic = cinder_conf.safe_get(utils.LOAD_LOOKBACK) lb_diagnostic = cinder_conf.safe_get(utils.LOAD_LOOKBACK)
if not lb_diagnostic: if not lb_diagnostic:
LOG.warning( LOG.warning(
"Diagnostic look back window not set in cinder.conf, " "Diagnostic look back window not set in cinder.conf, "
"reverting to default value of 60 for most recent hour of " "reverting to default value of 60 for most recent hour of "
"metrics.") "metrics.")
lb_diagnostic = 60 lb_diagnostic = 60
elif lb_diagnostic < 0 or lb_diagnostic > 1440: elif lb_diagnostic < 0 or lb_diagnostic > 1440:
LOG.warning( LOG.warning(
"Diagnostic look back window '%(lb)s' is not within the " "Diagnostic look back window '%(lb)s' is not within the "
"minimum and maximum range 0-1440, reverting to default " "minimum and maximum range 0-1440, reverting to default "
"value of 60 for most recent hour of metrics.", { "value of 60 for most recent hour of metrics.", {
'lb': lb_diagnostic}) 'lb': lb_diagnostic})
lb_diagnostic = 60 lb_diagnostic = 60
# Get real-time metrics look back window # Get real-time metrics look back window
lb_real_time = cinder_conf.safe_get(utils.LOAD_LOOKBACK_RT) lb_real_time = cinder_conf.safe_get(utils.LOAD_LOOKBACK_RT)
if rt_enabled: if rt_enabled:
if not lb_real_time: if not lb_real_time:
LOG.warning( LOG.warning(
"Real-time look back window not set in cinder.conf, " "Real-time look back window not set in cinder.conf, "
"reverting to default value of 1 for for most recent " "reverting to default value of 1 for for most recent "
"minute of metrics.") "minute of metrics.")
lb_real_time = 1 lb_real_time = 1
elif lb_real_time < 1 or lb_real_time > 60: elif lb_real_time < 1 or lb_real_time > 60:
LOG.warning( LOG.warning(
"Real-time look back window '%(lb)s' is not within the " "Real-time look back window '%(lb)s' is not within the "
"minimum and maximum range 1-60, reverting to default " "minimum and maximum range 1-60, reverting to default "
"value of 1 for for most recent minute of metrics.", { "value of 1 for for most recent minute of metrics.", {
'lb': lb_real_time}) 'lb': lb_real_time})
lb_real_time = 1 lb_real_time = 1
# Get Port Group metric for load calculation # Get Port Group metric for load calculation
pg_metric = cinder_conf.safe_get(utils.PORT_GROUP_LOAD_METRIC) pg_metric = cinder_conf.safe_get(utils.PORT_GROUP_LOAD_METRIC)
if not pg_metric: if not pg_metric:
LOG.warning( LOG.warning(
"Port Group performance metric not set in cinder.conf, " "Port Group performance metric not set in cinder.conf, "
"reverting to default metric 'PercentBusy'.") "reverting to default metric 'PercentBusy'.")
pg_metric = 'PercentBusy' pg_metric = 'PercentBusy'
elif pg_metric not in utils.PG_METRICS: elif pg_metric not in utils.PG_METRICS:
LOG.warning( LOG.warning(
"Port Group performance metric selected for load " "Port Group performance metric selected for load "
"balancing '%(pg_met)s' is not valid, reverting to " "balancing '%(pg_met)s' is not valid, reverting to "
"default metric 'PercentBusy'.", { "default metric 'PercentBusy'.", {
'pg_met': pg_metric}) 'pg_met': pg_metric})
pg_metric = 'PercentBusy' pg_metric = 'PercentBusy'
# Get Port metric for load calculation # Get Port metric for load calculation
port_metric = cinder_conf.safe_get(utils.PORT_LOAD_METRIC) port_metric = cinder_conf.safe_get(utils.PORT_LOAD_METRIC)
valid_port_metrics = ( valid_port_metrics = (
utils.PORT_RT_METRICS if rt_enabled else utils.PORT_METRICS) utils.PORT_RT_METRICS if rt_enabled else utils.PORT_METRICS)
if not port_metric: if not port_metric:
LOG.warning( LOG.warning(
"Port performance metric not set in cinder.conf, " "Port performance metric not set in cinder.conf, "
"reverting to default metric 'PercentBusy'.") "reverting to default metric 'PercentBusy'.")
port_metric = 'PercentBusy' port_metric = 'PercentBusy'
elif port_metric not in valid_port_metrics: elif port_metric not in valid_port_metrics:
LOG.warning( LOG.warning(
"Port performance metric selected for load balancing " "Port performance metric selected for load balancing "
"'%(port_met)s' is not valid, reverting to default metric " "'%(port_met)s' is not valid, reverting to default metric "
"'PercentBusy'.", {'port_met': port_metric}) "'PercentBusy'.", {'port_met': port_metric})
port_metric = 'PercentBusy' port_metric = 'PercentBusy'
self.config = { self.config = {
'load_balance': lb_enabled, 'load_balance_rt': rt_enabled, 'load_balance': lb_enabled, 'load_balance_rt': rt_enabled,
'perf_registered': p_reg, 'rt_registered': rt_reg, 'perf_registered': p_reg, 'rt_registered': rt_reg,
'collection_interval': c_int, 'data_format': data_format, 'collection_interval': c_int, 'data_format': data_format,
'look_back': lb_diagnostic, 'look_back_rt': lb_real_time, 'look_back': lb_diagnostic, 'look_back_rt': lb_real_time,
'port_group_metric': pg_metric, 'port_metric': port_metric} 'port_group_metric': pg_metric, 'port_metric': port_metric}
def get_array_registration_details(self, array_id): def get_array_registration_details(self, array_id):
"""Get array performance registration details. """Get array performance registration details.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:returns: performance registered, real-time registered, :returns: performance registered, real-time registered,
collection interval -- bool, bool, int collection interval -- bool, bool, int
""" """
LOG.info("Retrieving array %(arr)s performance registration details.", LOG.info("Retrieving array %(arr)s performance registration details.",
{'arr': array_id}) {'arr': array_id})
array_reg_uri = self.rest.build_uri( array_reg_uri = self.rest.build_uri(
category=utils.PERFORMANCE, resource_level=utils.ARRAY_PERF, category=utils.PERFORMANCE, resource_level=utils.ARRAY_PERF,
resource_type=utils.REG_DETAILS, resource_type_id=array_id, resource_type=utils.REG_DETAILS, resource_type_id=array_id,
no_version=True) no_version=True)
reg_details = self.rest.get_request( reg_details = self.rest.get_request(
target_uri=array_reg_uri, target_uri=array_reg_uri,
resource_type='Array registration details') resource_type='Array registration details')
array_reg_info = reg_details.get(utils.REG_DETAILS_INFO)[0] array_reg_info = reg_details.get(utils.REG_DETAILS_INFO)[0]
perf_registered = array_reg_info.get(utils.DIAGNOSTIC) perf_registered = array_reg_info.get(utils.DIAGNOSTIC)
real_time_registered = array_reg_info.get(utils.REAL_TIME) real_time_registered = array_reg_info.get(utils.REAL_TIME)
collection_interval = array_reg_info.get(utils.COLLECTION_INT) collection_interval = array_reg_info.get(utils.COLLECTION_INT)
return perf_registered, real_time_registered, collection_interval return perf_registered, real_time_registered, collection_interval
def get_array_performance_keys(self, array_id): def get_array_performance_keys(self, array_id):
"""Get array performance keys (first and last available timestamps). """Get array performance keys (first and last available timestamps).
:param array_id: the array serial number :param array_id: the array serial number
:returns: first date, last date -- int, int :returns: first date, last date -- int, int
""" """
LOG.debug("Retrieving array %(arr)s performance keys.", LOG.debug("Retrieving array %(arr)s performance keys.",
{'arr': array_id}) {'arr': array_id})
array_keys_uri = self.rest.build_uri( array_keys_uri = self.rest.build_uri(
category=utils.PERFORMANCE, resource_level=utils.ARRAY_PERF, category=utils.PERFORMANCE, resource_level=utils.ARRAY_PERF,
resource_type=utils.KEYS, no_version=True) resource_type=utils.KEYS, no_version=True)
array_keys = self.rest.get_request( array_keys = self.rest.get_request(
target_uri=array_keys_uri, resource_type='Array performance keys') target_uri=array_keys_uri, resource_type='Array performance keys')
env_symm_info = array_keys.get(utils.ARRAY_INFO) env_symm_info = array_keys.get(utils.ARRAY_INFO)
f_date, l_date = None, None f_date, l_date = None, None
for symm in env_symm_info: for symm in env_symm_info:
if symm.get(utils.SYMM_ID) == array_id: if symm.get(utils.SYMM_ID) == array_id:
f_date, l_date = symm.get(utils.F_DATE), symm.get(utils.L_DATE) f_date, l_date = symm.get(utils.F_DATE), symm.get(utils.L_DATE)
return f_date, l_date return f_date, l_date
@staticmethod @staticmethod
def _get_look_back_window_interval_timestamp(l_date, lb_window): def _get_look_back_window_interval_timestamp(l_date, lb_window):
"""Get first date value when calculated from last date and window. """Get first date value when calculated from last date and window.
:param l_date: the last (most recent) timestamp -- int :param l_date: the last (most recent) timestamp -- int
:param lb_window: the look back window in minutes -- int :param lb_window: the look back window in minutes -- int
:returns: the first timestamp -- int :returns: the first timestamp -- int
""" """
return l_date - (utils.ONE_MINUTE * lb_window) return l_date - (utils.ONE_MINUTE * lb_window)
@staticmethod @staticmethod
def _process_load(performance_data, metric): def _process_load(performance_data, metric):
"""Process the load for a given performance response, return average. """Process the load for a given performance response, return average.
:param performance_data: raw performance data from REST API -- dict :param performance_data: raw performance data from REST API -- dict
:param metric: performance metric in use -- str :param metric: performance metric in use -- str
:returns: range average, range total, interval count -- float, int, int :returns: range average, range total, interval count -- float, int, int
""" """
data = performance_data.get(utils.RESULT_LIST) data = performance_data.get(utils.RESULT_LIST)
result = data.get(utils.RESULT) result = data.get(utils.RESULT)
total = 0 total = 0
for timestamp in result: for timestamp in result:
total += timestamp.get(metric) total += timestamp.get(metric)
return total / len(result), total, len(result) return total / len(result), total, len(result)
def _get_port_group_performance_stats( def _get_port_group_performance_stats(
self, array_id, port_group_id, f_date, l_date, metric, self, array_id, port_group_id, f_date, l_date, metric,
data_format): data_format):
"""Get performance data for a given port group and performance metric. """Get performance data for a given port group and performance metric.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:param port_group_id: the port group id -- str :param port_group_id: the port group id -- str
:param f_date: first date for stats -- int :param f_date: first date for stats -- int
:param l_date: last date for stats -- int :param l_date: last date for stats -- int
:param metric: performance metric -- str :param metric: performance metric -- str
:param data_format: performance data format -- str :param data_format: performance data format -- str
:returns: range average, range total, interval count -- float, float, :returns: range average, range total, interval count -- float, float,
int int
""" """
request_body = { request_body = {
utils.SYMM_ID: array_id, utils.PORT_GROUP_ID: port_group_id, utils.SYMM_ID: array_id, utils.PORT_GROUP_ID: port_group_id,
utils.S_DATE: f_date, utils.E_DATE: l_date, utils.S_DATE: f_date, utils.E_DATE: l_date,
utils.DATA_FORMAT: data_format, utils.METRICS: [metric]} utils.DATA_FORMAT: data_format, utils.METRICS: [metric]}
port_group_uri = self.rest.build_uri( port_group_uri = self.rest.build_uri(
category=utils.PERFORMANCE, resource_level=utils.PORT_GROUP, category=utils.PERFORMANCE, resource_level=utils.PORT_GROUP,
resource_type=utils.METRICS, no_version=True) resource_type=utils.METRICS, no_version=True)
result = self.rest.post_request( result = self.rest.post_request(
port_group_uri, 'Port Group performance metrics', port_group_uri, 'Port Group performance metrics',
request_body) request_body)
return self._process_load(result, metric) return self._process_load(result, metric)
def _get_port_performance_stats( def _get_port_performance_stats(
self, array_id, director_id, port_id, f_date, l_date, metric, self, array_id, director_id, port_id, f_date, l_date, metric,
data_format=None, real_time=False): data_format=None, real_time=False):
"""Get performance data for a given port and performance metric. """Get performance data for a given port and performance metric.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:param director_id: the director id -- str :param director_id: the director id -- str
:param port_id: the port id -- str :param port_id: the port id -- str
:param f_date: first date for stats -- int :param f_date: first date for stats -- int
:param l_date: last date for stats -- int :param l_date: last date for stats -- int
:param metric: performance metric -- str :param metric: performance metric -- str
:param data_format: performance data format -- str :param data_format: performance data format -- str
:param real_time: if metrics are real-time -- bool :param real_time: if metrics are real-time -- bool
:returns: range average, range total, interval count -- float, float, :returns: range average, range total, interval count -- float, float,
int int
""" """
if real_time: if real_time:
target_uri = self.rest.build_uri( target_uri = self.rest.build_uri(
category=utils.PERFORMANCE, resource_level=utils.REAL_TIME, category=utils.PERFORMANCE, resource_level=utils.REAL_TIME,
resource_type=utils.METRICS, no_version=True) resource_type=utils.METRICS, no_version=True)
res_type = 'real-time' res_type = 'real-time'
dir_port = ('%(dir)s:%(port)s' % {'dir': director_id, dir_port = ('%(dir)s:%(port)s' % {'dir': director_id,
'port': port_id}) 'port': port_id})
request_body = { request_body = {
utils.SYMM_ID: array_id, utils.INST_ID: dir_port, utils.SYMM_ID: array_id, utils.INST_ID: dir_port,
utils.S_DATE: f_date, utils.E_DATE: l_date, utils.S_DATE: f_date, utils.E_DATE: l_date,
utils.CAT: utils.FE_PORT_RT, utils.METRICS: [metric]} utils.CAT: utils.FE_PORT_RT, utils.METRICS: [metric]}
else: else:
target_uri = self.rest.build_uri( target_uri = self.rest.build_uri(
category=utils.PERFORMANCE, resource_level=utils.FE_PORT_DIAG, category=utils.PERFORMANCE, resource_level=utils.FE_PORT_DIAG,
resource_type=utils.METRICS, no_version=True) resource_type=utils.METRICS, no_version=True)
res_type = 'diagnostic' res_type = 'diagnostic'
request_body = { request_body = {
utils.SYMM_ID: array_id, utils.SYMM_ID: array_id,
utils.DIR_ID: director_id, utils.PORT_ID: port_id, utils.DIR_ID: director_id, utils.PORT_ID: port_id,
utils.S_DATE: f_date, utils.E_DATE: l_date, utils.S_DATE: f_date, utils.E_DATE: l_date,
utils.DATA_FORMAT: data_format, utils.METRICS: [metric]} utils.DATA_FORMAT: data_format, utils.METRICS: [metric]}
resource = '%(res)s Port performance metrics' % {'res': res_type} resource = '%(res)s Port performance metrics' % {'res': res_type}
result = self.rest.post_request( result = self.rest.post_request(
target_uri, resource, request_body) target_uri, resource, request_body)
return self._process_load(result, metric) return self._process_load(result, metric)
def process_port_group_load( def process_port_group_load(
self, array_id, port_groups, max_load=False): self, array_id, port_groups, max_load=False):
"""Calculate the load for one or more port groups. """Calculate the load for one or more port groups.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:param port_groups: port group names -- list :param port_groups: port group names -- list
:param max_load: if max load port group should be returned -- bool :param max_load: if max load port group should be returned -- bool
:returns: low/max avg, metric, port group -- tuple(float, str, str) :returns: low/max avg, metric, port group -- tuple(float, str, str)
""" """
LOG.info("Calculating array %(arr)s load for Port Groups %(pg)s.", LOG.info("Calculating array %(arr)s load for Port Groups %(pg)s.",
{'arr': array_id, 'pg': port_groups}) {'arr': array_id, 'pg': port_groups})
data_format = self.config.get('data_format') data_format = self.config.get('data_format')
lb_window = self.config.get('look_back') lb_window = self.config.get('look_back')
pg_metric = self.config.get('port_group_metric') pg_metric = self.config.get('port_group_metric')
__, l_date = self.get_array_performance_keys(array_id) __, l_date = self.get_array_performance_keys(array_id)
f_date = self._get_look_back_window_interval_timestamp( f_date = self._get_look_back_window_interval_timestamp(
l_date, lb_window) l_date, lb_window)
heap_low, heap_high = [], [] heap_low, heap_high = [], []
start_time = time.time() start_time = time.time()
for pg in port_groups: for pg in port_groups:
avg, total, cnt = self._get_port_group_performance_stats( avg, total, cnt = self._get_port_group_performance_stats(
array_id, pg, f_date, l_date, pg_metric, data_format) array_id, pg, f_date, l_date, pg_metric, data_format)
LOG.debug( LOG.debug(
"Port Group '%(pg)s' %(df)s %(met)s load for %(interval)s min " "Port Group '%(pg)s' %(df)s %(met)s load for %(interval)s min "
"interval: %(avg)s", "interval: %(avg)s",
{'pg': pg, 'df': data_format, 'met': pg_metric, {'pg': pg, 'df': data_format, 'met': pg_metric,
'interval': lb_window, 'avg': avg}) 'interval': lb_window, 'avg': avg})
# Add PG average to lowest load heap # Add PG average to lowest load heap
heappush(heap_low, (avg, pg_metric, pg)) heappush(heap_low, (avg, pg_metric, pg))
# Add inverse PG average to highest load heap # Add inverse PG average to highest load heap
heappush(heap_high, (-avg, pg_metric, pg)) heappush(heap_high, (-avg, pg_metric, pg))
LOG.debug("Time taken to analyse Port Group performance: %(t)ss", LOG.debug("Time taken to analyse Port Group performance: %(t)ss",
{'t': time.time() - start_time}) {'t': time.time() - start_time})
return heappop(heap_high) if max_load else heappop(heap_low) return heappop(heap_high) if max_load else heappop(heap_low)
def process_port_load(self, array_id, ports, max_load=False): def process_port_load(self, array_id, ports, max_load=False):
"""Calculate the load for one or more ports. """Calculate the load for one or more ports.
:param array_id: the array serial number -- str :param array_id: the array serial number -- str
:param ports: physical dir:port names -- list :param ports: physical dir:port names -- list
:param max_load: if max load port should be returned -- bool :param max_load: if max load port should be returned -- bool
:returns: low/max avg, metric, port -- tuple(float, str, str) :returns: low/max avg, metric, port -- tuple(float, str, str)
""" """
LOG.info("Calculating array %(arr)s load for Ports %(port)s.", LOG.info("Calculating array %(arr)s load for Ports %(port)s.",
{'arr': array_id, 'port': ports}) {'arr': array_id, 'port': ports})
rt_enabled = self.config.get('load_balance_rt') rt_enabled = self.config.get('load_balance_rt')
rt_registered = self.config.get('rt_registered') rt_registered = self.config.get('rt_registered')
if rt_enabled and rt_registered: if rt_enabled and rt_registered:
real_time, data_format = True, None real_time, data_format = True, None
lb_window = self.config.get('look_back_rt') lb_window = self.config.get('look_back_rt')
else: else:
real_time, data_format = False, self.config.get('data_format') real_time, data_format = False, self.config.get('data_format')
lb_window = self.config.get('look_back') lb_window = self.config.get('look_back')
port_metric = self.config.get('port_metric') port_metric = self.config.get('port_metric')
__, l_date = self.get_array_performance_keys(array_id) __, l_date = self.get_array_performance_keys(array_id)
f_date = self._get_look_back_window_interval_timestamp( f_date = self._get_look_back_window_interval_timestamp(
l_date, lb_window) l_date, lb_window)
heap_low, heap_high = [], [] heap_low, heap_high = [], []
start_time = time.time() start_time = time.time()
for port in ports: for port in ports:
dir_id = port.split(':')[0] dir_id = port.split(':')[0]
port_no = port.split(':')[1] port_no = port.split(':')[1]
avg, total, cnt = self._get_port_performance_stats( avg, total, cnt = self._get_port_performance_stats(
array_id, dir_id, port_no, f_date, l_date, port_metric, array_id, dir_id, port_no, f_date, l_date, port_metric,
data_format, real_time=real_time) data_format, real_time=real_time)
LOG.debug( LOG.debug(
"Port '%(dir)s:%(port)s' %(df)s %(met)s load for %(int)s min " "Port '%(dir)s:%(port)s' %(df)s %(met)s load for %(int)s min "
"interval: %(avg)s", "interval: %(avg)s",
{'dir': dir_id, 'port': port_no, {'dir': dir_id, 'port': port_no,
'df': data_format if data_format else '', 'df': data_format if data_format else '',
'met': port_metric, 'int': lb_window, 'avg': avg}) 'met': port_metric, 'int': lb_window, 'avg': avg})
# Add PG average to lowest load heap # Add PG average to lowest load heap
heappush(heap_low, (avg, port_metric, port)) heappush(heap_low, (avg, port_metric, port))
# Add inverse PG average to highest load heap # Add inverse PG average to highest load heap
heappush(heap_high, (-avg, port_metric, port)) heappush(heap_high, (-avg, port_metric, port))
LOG.debug("Time taken to analyse Port Group performance: %(t)ss", LOG.debug("Time taken to analyse Port Group performance: %(t)ss",
{'t': time.time() - start_time}) {'t': time.time() - start_time})
return heappop(heap_high) if max_load else heappop(heap_low) return heappop(heap_high) if max_load else heappop(heap_low)

@ -1,4 +1,4 @@
--- ---
features: features:
- Support for snapshot backup using the optimal path in - Support for snapshot backup using the optimal path in
Huawei driver. Huawei driver.

@ -1,3 +1,3 @@
--- ---
features: features:
- New Cinder Hitachi driver based on REST API for Hitachi VSP storages. - New Cinder Hitachi driver based on REST API for Hitachi VSP storages.

@ -1,16 +1,16 @@
--- ---
features: features:
- Hitachi VSP drivers have a new config option - Hitachi VSP drivers have a new config option
``vsp_compute_target_ports`` to specify IDs ``vsp_compute_target_ports`` to specify IDs
of the storage ports used to attach volumes of the storage ports used to attach volumes
to compute nodes. The default is the value to compute nodes. The default is the value
specified for the existing ``vsp_target_ports`` specified for the existing ``vsp_target_ports``
option. Either or both of ``vsp_compute_target_ports`` option. Either or both of ``vsp_compute_target_ports``
and ``vsp_target_ports`` must be specified. and ``vsp_target_ports`` must be specified.
- Hitachi VSP drivers have a new config option - Hitachi VSP drivers have a new config option
``vsp_horcm_pair_target_ports`` to specify IDs of the ``vsp_horcm_pair_target_ports`` to specify IDs of the
storage ports used to copy volumes by Shadow Image or storage ports used to copy volumes by Shadow Image or
Thin Image. The default is the value specified for Thin Image. The default is the value specified for
the existing ``vsp_target_ports`` option. Either the existing ``vsp_target_ports`` option. Either
or both of ``vsp_horcm_pair_target_ports`` and or both of ``vsp_horcm_pair_target_ports`` and
``vsp_target_ports`` must be specified. ``vsp_target_ports`` must be specified.

@ -1,3 +1,3 @@
--- ---
features: features:
- Optimize backend reporting capabilities for Huawei drivers. - Optimize backend reporting capabilities for Huawei drivers.

@ -1,3 +1,3 @@
--- ---
features: features:
- Add CG capability to generic volume groups in Huawei driver. - Add CG capability to generic volume groups in Huawei driver.

@ -1,3 +1,3 @@
--- ---
upgrade: upgrade:
- Support for iSCSI multipath in Huawei driver. - Support for iSCSI multipath in Huawei driver.

@ -1,4 +1,4 @@
--- ---
features: features:
- Add support for reporting pool disk type in Huawei - Add support for reporting pool disk type in Huawei
driver. driver.

@ -1,3 +1,3 @@
--- ---
upgrade: upgrade:
- Support iSCSI configuration in replication in Huawei driver. - Support iSCSI configuration in replication in Huawei driver.

@ -1,3 +1,3 @@
--- ---
features: features:
- Added consistency group support to the Huawei driver. - Added consistency group support to the Huawei driver.