# Copyright (c) 2020 Dell Inc. or its subsidiaries.
#
# 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.
"""real_time.py."""
import logging
import time
from PyU4V import common
from PyU4V.utils import exception
from PyU4V.utils import performance_constants as pc
LOG = logging.getLogger(__name__)
[docs]
class RealTimeFunctions(object):
"""PerformanceFunctions."""
def __init__(self, array_id, rest_client):
"""__init__."""
self.common = common.CommonFunctions(rest_client)
self.post_request = self.common.create_resource
self.get_request = self.common.get_resource
self.array_id = array_id
self.recency = 0
[docs]
def set_array_id(self, array_id):
"""Set the array id.
:param array_id: array id -- str
"""
self.array_id = array_id
[docs]
def set_recency(self, minutes):
"""Set the recency value in minutes.
:param minutes: recency minutes -- int
"""
self.recency = int(minutes)
[docs]
def is_timestamp_current(self, timestamp, minutes=None):
"""Check if the timestamp is less than a user specified set of minutes.
If no minutes value is provided, self.recency is used. Seven minutes
is recommended to provide a small amount of time for the STP daemon to
record the next set of metrics in five minute intervals.
:param timestamp: timestamp in milliseconds since epoch -- int
:param minutes: timestamp recency in minutes -- int
:returns: if timestamp is less than recency value -- bool
"""
r = minutes if isinstance(minutes, int) else self.recency
return (int(time.time()) * 1000) - timestamp < r * pc.ONE_MINUTE
[docs]
def get_categories(self, array_id=None):
"""Get a list of real-time supported performance categories.
:param array_id: array serial number -- str
:returns: categories -- list
"""
array_id = array_id if array_id else self.array_id
response = self.get_request(
no_version=True, category=pc.PERFORMANCE,
resource_level=pc.REAL_TIME, resource_type=pc.HELP,
resoruce=array_id, object_type=pc.CATEGORIES)
return response.get(pc.CATEGORY_NAME, list()) if response else list()
[docs]
def get_category_metrics(self, category, array_id=None):
"""Get metrics available for a real-time performance category.
:param category: real-time performance category -- str
:param array_id: array serial number -- str
:returns: metrics -- list
"""
array_id = array_id if array_id else self.array_id
response = self.get_request(
no_version=True, category=pc.PERFORMANCE,
resource_level=pc.REAL_TIME, resource_type=pc.HELP,
resource_type_id=array_id, resource=category,
object_type=pc.METRICS)
return response.get(pc.METRIC_NAME, list()) if response else list()
[docs]
def get_timestamps(self, array_id=None):
"""Get real-time performance timestamps for array(s).
:param array_id: array serial number -- str
:returns: array timestamp info -- list
"""
response = self.get_request(
no_version=True, category=pc.PERFORMANCE,
resource_level=pc.REAL_TIME, resource_type=pc.HELP,
resource=pc.TIMES)
timestamps = response.get(
pc.ARRAY_INFO, list()) if response else list()
if array_id and timestamps:
for array_info in timestamps:
if array_info.get(pc.SYMM_ID) == array_id:
return [array_info]
return timestamps
[docs]
def get_category_keys(self, category, array_id=None):
"""Get category keys valid for real-time metrics collection.
:param category: real-time performance category -- str
:param array_id: array serial number -- str
:returns: category keys -- list
"""
array_id = self.array_id if not array_id else array_id
request_params = {pc.SYMM_ID: array_id, pc.CATEGORY: category}
try:
response = self.post_request(
no_version=True, category=pc.PERFORMANCE,
resource_level=pc.REAL_TIME, resource_type=pc.KEYS,
payload=request_params)
except Exception as e:
logging.error(f"Error in get_category_keys: {e}")
return list()
return response.get(pc.KEYS, list()) if response else list()
def _validate_real_time_input(
self, start_date, end_date, category, metrics, instance_id):
"""Validate user input for real-time metrics collection.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param category: category id -- str
:param metrics: performance metrics -- list
:param instance_id: instance id -- str
:raises: VolumeBackendAPIException, InvalidInputException
"""
delta, msg = end_date - start_date, None
# Category validation
if category not in self.get_categories():
# Allow for no 's' at the end of StorageGroups, StorageGroup is
# still valid but not returned in category list
if category != pc.SG:
msg = (
'Real-time performance category "{user_cat}" is not '
'one of {uni_cat}.'.format(
user_cat=category, uni_cat=self.get_categories()))
# Metrics validation
elif metrics != [pc.All_CAP] and not (
all(metric in self.get_category_metrics(
category) for metric in metrics)):
msg = (
'The supplied real-time metrics {user_met} are not '
'valid. Valid options are "All", and one or more of '
'{uni_met}'.format(
user_met=metrics,
uni_met=self.get_category_metrics(category)))
# Required input validation
elif category != pc.ARRAY and not instance_id:
msg = ('For real-time performance data other than from the '
'"Array" category an instance_id must be specified.')
# Instance ID key validation against known real-time keys
elif instance_id and instance_id not in self.get_category_keys(
category=category):
msg = (
'Instance ID "{inst}" is not one of {cat} real-time '
'performance keys {uni_keys}'.format(
inst=instance_id, cat=category,
uni_keys=self.get_category_keys(category=category)))
# Timestamp validation
elif not isinstance(end_date, int) or not isinstance(start_date, int):
msg = ('Start and end dates must be of type <int> and in '
'milliseconds since epoch format.')
elif delta < pc.ONE_MINUTE:
ct, one_min = int(time.time()) * 1000, pc.ONE_MINUTE
if (ct - end_date < one_min) or (ct - start_date < one_min):
msg = ('Real-time timestamps cannot be for intervals of less '
'than one minute if the start or end timestamps are '
'within one minute of local time.')
elif delta > pc.ONE_HOUR:
msg = ('It is not possible to query for more than one hour of '
'real-time performance data in one request.')
elif self.recency:
if not self.is_timestamp_current(int(end_date), self.recency):
msg = ('Timestamp "{t}" failed recency check of {rec} '
'minutes.'.format(t=end_date, rec=self.recency))
if msg:
LOG.error(msg)
raise exception.InvalidInputException(msg)
# Real-time category specific calls
[docs]
def get_array_metrics(self):
"""Get array real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.ARRAY)
[docs]
def get_array_keys(self):
"""Get array IDs which are registered for real-time data.
:returns: array IDs -- list
"""
return self.get_category_keys(pc.ARRAY)
[docs]
def get_array_stats(self, start_date, end_date, metrics, array_id=None):
"""List real-time data for specified array.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.ARRAY,
metrics=metrics, array_id=array_id)
[docs]
def get_backend_director_metrics(self):
"""Get backend director real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.BE_DIR)
[docs]
def get_backend_director_keys(self, array_id=None):
"""Get backend director IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: backend director IDs -- list
"""
return self.get_category_keys(pc.BE_DIR, array_id)
[docs]
def get_backend_director_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified backend director.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: backend director id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.BE_DIR,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_backend_port_metrics(self):
"""Get backend port real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.BE_PORT)
[docs]
def get_backend_port_keys(self, array_id=None):
"""Get backend dir/port IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: backend port IDs -- list
"""
return self.get_category_keys(pc.BE_PORT, array_id)
[docs]
def get_backend_port_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified backend port.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: backend dir/port id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.BE_PORT,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_external_director_metrics(self):
"""Get external director real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.EXT_DIR)
[docs]
def get_external_director_keys(self, array_id=None):
"""Get external director IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: external director IDs -- list
"""
return self.get_category_keys(pc.EXT_DIR, array_id)
[docs]
def get_external_director_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified external director.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: external director id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.EXT_DIR,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_frontend_director_metrics(self):
"""Get frontend director real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.FE_DIR)
[docs]
def get_frontend_director_keys(self, array_id=None):
"""Get frontend director IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: frontend director IDs -- list
"""
return self.get_category_keys(pc.FE_DIR, array_id)
[docs]
def get_frontend_director_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified frontend director.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: frontend director id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date,
category=pc.FE_DIR, metrics=metrics, array_id=array_id,
instance_id=instance_id)
[docs]
def get_frontend_port_metrics(self):
"""Get frontend port real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.FE_PORT)
[docs]
def get_frontend_port_keys(self, array_id=None):
"""Get frontend dir/port IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: frontend port IDs -- list
"""
return self.get_category_keys(pc.FE_PORT, array_id)
[docs]
def get_frontend_port_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified frontend port.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: frontend dir/port id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.FE_PORT,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_rdf_director_metrics(self):
"""Get rdf director real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.RDF_DIR)
[docs]
def get_rdf_director_keys(self, array_id=None):
"""Get rdf director IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: rdf director IDs -- list
"""
return self.get_category_keys(pc.RDF_DIR, array_id)
[docs]
def get_rdf_director_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified backend director.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: rdf director id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.RDF_DIR,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_rdf_port_metrics(self):
"""Get rdf port real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.RDF_PORT)
[docs]
def get_rdf_port_keys(self, array_id=None):
"""Get rdf dir/port IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: rdf port IDs -- list
"""
return self.get_category_keys(pc.RDF_PORT, array_id)
[docs]
def get_rdf_port_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified rdf port.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: rdf dir/port id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.RDF_PORT,
metrics=metrics, array_id=array_id, instance_id=instance_id)
[docs]
def get_storage_group_metrics(self):
"""Get storage group real-time performance metrics.
:returns: metrics -- list
"""
return self.get_category_metrics(pc.SG)
[docs]
def get_storage_group_keys(self, array_id=None):
"""Get storage group IDs which are registered for real-time data.
:param array_id: array serial number -- str
:returns: backend director IDs -- list
"""
return self.get_category_keys(pc.SG, array_id)
[docs]
def get_storage_group_stats(self, start_date, end_date, metrics,
instance_id, array_id=None):
"""List real-time data for specified storage group.
:param start_date: timestamp in milliseconds since epoch -- int
:param end_date: timestamp in milliseconds since epoch -- int
:param metrics: performance metrics, options are individual metrics,
a list of metrics, or 'ALL' for all metrics -- str/list
:param instance_id: storage group id -- str
:param array_id: array serial number -- str
:returns: real-time performance data -- dict
"""
return self.get_performance_data(
start_date=start_date, end_date=end_date, category=pc.SG,
metrics=metrics, array_id=array_id, instance_id=instance_id)