Skip to content

Commit

Permalink
add runbooks for internal mode and provider mode clusters (red-hat-st…
Browse files Browse the repository at this point in the history
…orage#9673)

* add runbooks for internal mode and provider mode

Signed-off-by: Daniel Osypenko <[email protected]>
  • Loading branch information
DanielOsypenko authored and fbalak committed Apr 24, 2024
1 parent cb49ab4 commit dd77fae
Show file tree
Hide file tree
Showing 7 changed files with 437 additions and 4 deletions.
21 changes: 21 additions & 0 deletions ocs_ci/ocs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,27 @@
ALERT_STORAGECLIENTINCOMPATIBLEOPERATORVERSION = (
"StorageClientIncompatibleOperatorVersion"
)
ALERT_CEPH_OSD_VERSION_MISMATCH = "CephOSDVersionMismatch"
ALERT_PERSISTENT_VOLUME_USAGE_CRITICAL = "PersistentVolumeUsageCritical"
ALERT_CEPH_CLUSTER_READ_ONLY = "CephClusterReadOnly"
ALERT_CEPH_MON_VERSION_MISMATCH = "CephMonVersionMismatch"
ALERT_CEPH_POOL_QUOTA_BYTES_CRITICALLY_EXHAUSTED = (
"CephPoolQuotaBytesCriticallyExhausted"
)
ALERT_CEPH_POOL_QUOTA_BYTES_NEAR_EXHAUSTION = "CephPoolQuotaBytesNearExhaustion"
ALERT_CEPH_MDS_MISSING_REPLICAS = "CephMdsMissingReplicas"
ALERT_CEPH_MON_HIGH_NUMBER_OF_LEADER_CHANGES = "CephMonHighNumberOfLeaderChanges"
ALERT_CEPH_OSD_CRITICALLY_FULL = "CephOSDCriticallyFull"
ALERT_OBC_QUOTA_OBJECTS_ALERT = "ObcQuotaObjectsAlert"
ALERT_OBC_QUOTA_BYTES_EXHAUSED_ALERT = "ObcQuotaBytesExhausedAlert"
ALERT_ODF_RBD_CLIENT_BLOCKED = "ODFRBDClientBlocked"
ALERT_ODF_MIRROR_DAEMON_STATUS = "OdfMirrorDaemonStatus"
ALERT_ODF_MIRRORING_IMAGE_HEALTH = "OdfPoolMirroringImageHealth"
ALERT_CEPH_OSD_FLAPPING = "CephOSDFlapping"
ALERT_CEPH_OSD_NEAR_FULL = "CephOSDNearFull"
ALERT_PERSISTENT_VOLUME_USAGE_NEAR_FULL = "PersistentVolumeUsageNearFull"
ALERT_ODF_PERSISTENT_VOLUME_MIRROR_STATUS = "ODFPersistentVolumeMirrorStatus"
ALERT_OBC_QUOTA_BYTES_ALERT = "ObcQuotaBytesAlert"

# OCS Deployment related constants
OPERATOR_NODE_LABEL = "cluster.ocs.openshift.io/openshift-storage=''"
Expand Down
1 change: 1 addition & 0 deletions ocs_ci/ocs/ui/base_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def __init__(self):
locators, self.ocp_version, "add_capacity"
)
self.topology_loc = self.deep_get(locators, self.ocp_version, "topology")
self.alerting_loc = self.deep_get(locators, self.ocp_version, "alerting")

def __repr__(self):
return f"{self.__class__.__name__} Web Page"
Expand Down
259 changes: 259 additions & 0 deletions ocs_ci/ocs/ui/page_objects/alerting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import logging
import re
import hashlib
import requests
from ocs_ci.utility.retry import retry
from requests import TooManyRedirects, Timeout, HTTPError

from ocs_ci.ocs.ui.page_objects.page_navigator import PageNavigator
from ocs_ci.ocs.ui.page_objects.searchbar import SearchBar


logger = logging.getLogger(__name__)


class Runbook:
"""
Runbook object used for checking the runbook content.
May be instantiated with the runbook hash value as expected result for the test;
If the runbook hash value is not provided, text should be provided and the hash value will be calculated
"""

def __init__(self, text=None, runbook_hash=None):

if not text and not runbook_hash:
raise ValueError("Runbook text or hash value should be provided")

self.text = text
if not runbook_hash:
self.runbook_hash = hashlib.md5(self.text.strip().encode()).hexdigest()
else:
self.runbook_hash = runbook_hash

def __repr__(self):
"""
get base64 hash of the Raw runbook page content
"""

return self.runbook_hash

def __eq__(self, other):
"""
Compare two instances based on their hash values
"""
if isinstance(other, Runbook):
return self.runbook_hash == other.runbook_hash
return False

def __ne__(self, other):
"""
Compare two instances based on their hash values
"""
return not self.__eq__(other)

def check_text_content(self, headers, *args):
"""
Check if the text is valid
Returns:
bool: True if the text is valid, False otherwise
"""
if hasattr(self, "text") and not self.text:
raise ValueError(
"Runbook text is empty, this object instantiated with hash value as Expected result for the test "
"and should not be checked"
)
# Check if the text contains all the headers
for chapter in headers:
if not re.search(f"## {headers}", self.text):
logger.error(f"Chapter '{chapter}' is missing.")
return False

# Check additional text arguments if provided
for arg in args:
if not re.search(arg, self.text):
logger.error(f"arg '{arg}' is missing in the text.")
return False

return True


class Alerting(PageNavigator):
"""
Alerting page that contains 3 tabs - Alerts, Silences and Alerting Rules
The default tab is Alerts
"""

def __init__(self):
PageNavigator.__init__(self)

def nav_alerts(self):
"""
Navigate to Alerts tab. Default tab when opening the Alerting page
Returns:
Alerts: Alerts page
"""
logger.info("Navigate to Alerts tab")
self.do_click(self.validation_loc["alerts-tab-link"], enable_screenshot=True)
return Alerts()

def nav_silences(self):
"""
Navigate to Silences tab
Returns:
Silences: Silences page
"""
logger.info("Navigate to Silences tab")
self.do_click(self.alerting_loc["silences-tab-link"], enable_screenshot=True)
return Silences()

def nav_alerting_rules(self):
"""
Navigate to Alerting Rules tab
Returns:
AlertingRules: Alerting Rules page
"""
logger.info("Navigate to Alerting Rules tab")
self.do_click(
self.alerting_loc["alerting-rules-tab-link"], enable_screenshot=True
)
return AlertingRules()


class Alerts(Alerting, SearchBar):
"""
Alerts page object
"""

def __init__(self):
Alerting.__init__(self)
SearchBar.__init__(self)

def search_alert(self, search_text):
"""
Search for Alerts
Args:
search_text (str): Text to search
"""
self.search(search_text)


class Silences(Alerting):
"""Silences page object where all the silences are listed and new silences may be created."""

pass


class AlertingRules(Alerting, SearchBar):
"""
Alerting Rules page object. Contains all the alerts existing in the cluster that are being monitored.
Use 'oc get prometheusrules -n openshift-storage ocs-prometheus-rules -o yaml' to get the list of alerts
"""

def __init__(self):
Alerting.__init__(self)
SearchBar.__init__(self)

def navigate_alerting_rule_details(self, alert_name):
"""
Navigate to Alerting Rule Details
Args:
alert_name (str): Alert name
Returns:
AlertDetails: Alert Rule Details page
"""
logger.info(f"Navigate to Alerting Rule Details for {alert_name}")
self.clear_search()
self.search(alert_name)

from ocs_ci.ocs.ui.helpers_ui import format_locator

self.do_click(
format_locator(self.alerting_loc["alerting_rule_details_link"], alert_name),
avoid_stale=True,
)
return AlertDetails()


def convert_github_link_to_raw(link):
"""
Convert GitHub link to raw link
Args:
link (str): GitHub link
Returns:
Raw GitHub link or None if the link is not valid
"""
# Define pattern to match the GitHub link
pattern = r"https://github\.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)/blob/(?P<branch>[^/]+)/(?P<path>.+)"

# Match the pattern in the raw link
match = re.match(pattern, link)
if match:
owner = match.group("owner")
repo = match.group("repo")
branch = match.group("branch")
path = match.group("path")

# Construct the raw GitHub link
raw_github_link = (
f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}"
)
return raw_github_link
else:
return None


class AlertDetails(PageNavigator):
"""
Alert Details page object
"""

def __init__(self):
super().__init__()

def get_runbook_link(self):
"""
Get Runbook Link
Returns:
str: Runbook link
"""
return self.get_element_text(self.alerting_loc["runbook_link"])

def get_raw_runbook(self):
"""
Get Runbook
Returns:
Runbook: Runbook page
"""
runbook_link = self.get_runbook_link()
raw_github_link = convert_github_link_to_raw(runbook_link)
if raw_github_link:
logger.debug(f"Get Runbook from {raw_github_link}")
resp = retry(
(HTTPError, ConnectionError, Timeout, TooManyRedirects),
tries=3,
delay=10,
)(requests.get)(raw_github_link)
return Runbook(resp.text)
else:
logger.error("Invalid GitHub link")
return None
8 changes: 6 additions & 2 deletions ocs_ci/ocs/ui/page_objects/page_navigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,16 +297,20 @@ def navigate_alerting_page(self):
"""
logger.info("Navigate to Alerting Page")
self.choose_expanded_mode(mode=True, locator=self.page_nav["Monitoring"])
self.choose_expanded_mode(mode=True, locator=self.page_nav["observe"])
self.do_click(locator=self.page_nav["alerting_page"], enable_screenshot=False)

from ocs_ci.ocs.ui.page_objects.alerting import Alerts

return Alerts()

def navigate_metrics_page(self):
"""
Navigate to Metrics Page
"""
logger.info("Navigate to Metrics Page")
self.choose_expanded_mode(mode=True, locator=self.page_nav["Monitoring"])
self.choose_expanded_mode(mode=True, locator=self.page_nav["observe"])
self.do_click(locator=self.page_nav["metrics_page"], enable_screenshot=False)

def navigate_dashboards_page(self):
Expand Down
10 changes: 9 additions & 1 deletion ocs_ci/ocs/ui/page_objects/searchbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,13 @@ def search(self, search_text: str):
Args:
search_text (str): Text to search
"""
logger.info(f"Enter the text into search input: '{search_text}'")
logger.debug(f"Enter the text into search input: '{search_text}'")
self.do_send_keys(self.generic_locators["searchbar_input"], search_text)

def clear_search(self):
"""
Clear search input
"""
logger.info("Clear search input")
self.do_clear(self.generic_locators["searchbar_input"])
14 changes: 13 additions & 1 deletion ocs_ci/ocs/ui/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@
By.XPATH,
),
"object_storage": ("//a[normalize-space()='Object Storage']", By.XPATH),
"Monitoring": ("//button[text()='Monitoring']", By.XPATH),
"observe": ("//button[text()='Observe']", By.XPATH),
"alerting_page": ("Alerting", By.LINK_TEXT),
"metrics_page": ("Metrics", By.LINK_TEXT),
"dashboards_page": ("Dashboards", By.LINK_TEXT),
Expand Down Expand Up @@ -1757,6 +1757,16 @@
),
}


alerting = {
"alerts-tab-link": ("Alerts", By.LINK_TEXT),
"silences-tab-link": ("Silences", By.LINK_TEXT),
"alerting-rules-tab-link": ("Alerting rules", By.LINK_TEXT),
"runbook_link": ("//a[@class='co-external-link']", By.XPATH),
"alerting_rule_details_link": ("//a[normalize-space()='{}']", By.XPATH),
}


locators = {
"4.16": {
"login": {**login, **login_4_11, **login_4_14},
Expand Down Expand Up @@ -1804,6 +1814,7 @@
"bucketclass": bucketclass,
"topology": topology,
"mcg_stores": mcg_stores,
"alerting": alerting,
},
"4.15": {
"login": {**login, **login_4_11, **login_4_14},
Expand Down Expand Up @@ -1851,6 +1862,7 @@
"bucketclass": bucketclass,
"topology": topology,
"mcg_stores": mcg_stores,
"alerting": alerting,
},
"4.14": {
"login": {**login, **login_4_11, **login_4_14},
Expand Down
Loading

0 comments on commit dd77fae

Please sign in to comment.