[COMMIT scylla-cluster-tests master] fix(monitoring_ui): support monitoring branch-3.9

2 views
Skip to first unread message

Commit Bot

<bot@cloudius-systems.com>
unread,
Sep 19, 2021, 5:31:57 AM9/19/21
to scylladb-dev@googlegroups.com, Aleksandr Bykov
From: Aleksandr Bykov <alex....@scylladb.com>
Committer: Roy Dahan <r...@scylladb.com>
Branch: master

fix(monitoring_ui): support monitoring branch-3.9

Fix logcollector to support scylla-monitoring branch-3.9
Grafana was upgraded to v8, UI was changed.
Xpath was updated to support new UI elements
Added method to search dashboards by title. The reason
was that grafana v8. doesn't allow to get dashboard by
uri only by URL.

---
diff --git a/sdcm/logcollector.py b/sdcm/logcollector.py
--- a/sdcm/logcollector.py
+++ b/sdcm/logcollector.py
@@ -51,7 +51,6 @@
from sdcm.utils.get_username import get_username
from sdcm.utils.remotewebbrowser import RemoteBrowser, WebDriverContainerMixin

-
LOGGER = logging.getLogger(__name__)


@@ -384,33 +383,22 @@ def get_grafana_annotations(self, grafana_ip: str) -> str:
return ""

@staticmethod
- def dashboard_exists(grafana_ip, uid):
- """Check on Grafana server, that dashboard exists
-
- Send request to Grafana and validate that dashboard with uid
- provided as "uid" parameter is available and could be requested
-
- Arguments:
- node {CollectingNode} -- Remote host with grafana server
- uid {str} -- uid of grafana dashboard
+ @retrying(n=3, sleep_time=3, message="Search dashboard...", raise_on_exceeded=False)
+ def search_dashboard(grafana_ip: str, port: int, query: str) -> list:
+ search_api_url = f"http://{grafana_ip}:{port}/api/search?query={query}"
+ resp = requests.get(search_api_url)
+ if not resp.ok:
+ LOGGER.error("Search dashboards by query '%s' failed: %s %s", query, resp.status_code, resp.content)
+ return []
+ return resp.json()

- Returns:
- bool -- return True if exists, false otherwise
- """
- checked_dashboard_url = "http://{grafana_ip}:{grafana_port}/api/dashboards/db/{uid}"
- try:
- LOGGER.info(
- "Check dashboard: %s", checked_dashboard_url.format(grafana_ip=grafana_ip,
- grafana_port=MonitoringStack.grafana_port,
- uid=uid))
- res = requests.get(checked_dashboard_url.format(grafana_ip=grafana_ip,
- grafana_port=MonitoringStack.grafana_port,
- uid=uid))
- return bool(res.ok and res.json())
-
- except Exception as ex: # pylint: disable=broad-except
- LOGGER.warning("Error during checking if dashboard is exists. %s", ex)
- return False
+ @staticmethod
+ def get_dashboard_by_title(grafana_ip: str, port: int, title: str) -> Optional[dict]:
+ dashboards = MonitoringStack.search_dashboard(grafana_ip, port, title)
+ if not dashboards:
+ LOGGER.error("Dashboard with title '%s' was not found", title)
+ return None
+ return next((dashboard for dashboard in dashboards if title in dashboard["title"]), None)

def collect(self, node, local_dst, remote_dst=None, local_search_path=None):
local_archive = self.get_monitoring_data_stack(node, local_dst)
@@ -439,7 +427,7 @@ class GrafanaEntity(BaseMonitoringEntity): # pylint: disable=too-few-public-met
]

grafana_port = 3000
- grafana_entity_url_tmpl = "http://{node_ip}:{grafana_port}/{path}?from={st}&to=now&refresh=1d"
+ grafana_entity_url_tmpl = "http://{node_ip}:{grafana_port}{path}?from={st}&to=now&refresh=1d"
sct_base_path = get_sct_root_path()

def __init__(self, *args, **kwargs):
@@ -493,20 +481,18 @@ def get_grafana_screenshot(self, node, local_dst):
self.remote_browser = RemoteBrowser(node)
for dashboard in self.grafana_dashboards:
try:
- dashboard_exists = MonitoringStack.dashboard_exists(grafana_ip=normalize_ipv6_url(node.grafana_address),
- uid="-".join([dashboard.name,
- version])
- )
- if not dashboard_exists:
- version = "master"
-
- path = dashboard.path.format(
- version=version,
- dashboard_name=dashboard.name)
+ dashboard_metadata = MonitoringStack.get_dashboard_by_title(
+ grafana_ip=normalize_ipv6_url(node.grafana_address),
+ port=self.grafana_port,
+ title=dashboard.title)
+ if not dashboard_metadata:
+ LOGGER.error("Dashboard with title '%s' was not found", dashboard.title)
+ continue
+
grafana_url = self.grafana_entity_url_tmpl.format(
node_ip=normalize_ipv6_url(node.grafana_address),
grafana_port=self.grafana_port,
- path=path,
+ path=dashboard_metadata["url"],
st=self.start_time)
screenshot_path = os.path.join(local_dst,
"%s-%s-%s-%s.png" % (self.name,
@@ -560,18 +546,18 @@ def get_grafana_snapshot(self, node):
port=self.grafana_port).use_default_creds()
for dashboard in self.grafana_dashboards:
try:
- dashboard_exists = MonitoringStack.dashboard_exists(grafana_ip=normalize_ipv6_url(node.grafana_address),
- uid="-".join([dashboard.name, version]))
- if not dashboard_exists:
- version = "master"
-
- path = dashboard.path.format(
- version=version,
- dashboard_name=dashboard.name)
+ dashboard_metadata = MonitoringStack.get_dashboard_by_title(
+ grafana_ip=normalize_ipv6_url(node.grafana_address),
+ port=self.grafana_port,
+ title=dashboard.title)
+ if not dashboard_metadata:
+ LOGGER.error("Dashboard '%s' was not found", dashboard.title)
+ continue
+
grafana_url = self.grafana_entity_url_tmpl.format(
node_ip=normalize_ipv6_url(node.grafana_address),
grafana_port=self.grafana_port,
- path=path,
+ path=dashboard_metadata["url"],
st=self.start_time)
LOGGER.info("Get snapshot link for url %s", grafana_url)
self.remote_browser.open(grafana_url, dashboard.resolution)
diff --git a/sdcm/monitorstack/ui.py b/sdcm/monitorstack/ui.py
--- a/sdcm/monitorstack/ui.py
+++ b/sdcm/monitorstack/ui.py
@@ -55,7 +55,7 @@ def skip_set_new_password(self):


class Panel:
- xpath_tmpl = """//div[contains(@aria-label,'{name}') and contains(@class, 'panel-title-container')]"""
+ xpath_tmpl = """//header[contains(@aria-label,'{name}') and contains(@class, 'panel-title-container')]"""

def __init__(self, name):
self.name = name
@@ -75,23 +75,17 @@ def wait_loading(self, remote_browser):
WebDriverWait(remote_browser, UI_ELEMENT_LOAD_TIMEOUT).until(EC.invisibility_of_element(loading))
LOGGER.debug("Panel %s could be without data", self.name)
except exceptions.NoSuchElementException:
- LOGGER.debug("Check panel is loaded %s", self.name)
- list_of_childs = panel_elem.find_elements_by_xpath("child::*")
- assert len(list_of_childs) == 2, f"num of elements {len(list_of_childs)}"
LOGGER.debug("Panel %s loaded", self.name)
except exceptions.TimeoutException:
LOGGER.warning("Panel %s is still loading. Data on panel could displayed with delay", self.name)
- list_of_childs = panel_elem.find_elements_by_xpath("child::*")
- assert len(list_of_childs) == 3, f"num of elements {len(list_of_childs)}"
LOGGER.debug("Panel %s was not fully loaded", self.name)
LOGGER.info("Work with panel %s done", self.name)


class Snapshot: # pylint: disable=too-few-public-methods
locators_sequence = [
- (By.XPATH, # full xpath is set, no any explicit ids.
- """/html/body/grafana-app/div/div/react-container/div/div[1]/div[1]/div[4]/div/button"""),
- (By.XPATH, """//ul/li[contains(text(), "Snapshot")]"""),
+ (By.XPATH, """//button[contains(@aria-label, "Share dashboard or panel")]"""),
+ (By.XPATH, """//ul/li/a[contains(@aria-label, "Tab Snapshot") and contains(text(), "Snapshot")]"""),
(By.XPATH, """//button//span[contains(text(), "Publish to snapshot.raintank.io")]"""),
(By.XPATH, """//a[contains(@href, "https://snapshot.raintank.io")]""")
]
@@ -129,6 +123,7 @@ class Dashboard:
scroll_ready_locator: Tuple[By, str] = (By.XPATH, "//div[@class='scrollbar-view']")
panels: List[Panel]
scroll_step: int = 1000
+ title: str

def scroll_to_bottom(self, remote_browser):
WebDriverWait(remote_browser, UI_ELEMENT_LOAD_TIMEOUT).until(
@@ -154,19 +149,22 @@ def get_snapshot(remote_browser):
class OverviewDashboard(Dashboard):
name = 'overview'
path = 'd/overview-{version}/scylla-{dashboard_name}'
+ title = 'Overview'
resolution = '1920px*4000px'
panels = [Panel("Disk Size by DC"),
Panel("Running Compactions"),
Panel("Writes"),
Panel("Write Latencies"),
- Panel("Read/Write Timeouts by DC")]
+ Panel("Read Timeouts by DC"),
+ Panel("Write Timeouts by DC")]


class ServerMetricsNemesisDashboard(Dashboard):
if test_name := get_test_name():
test_name = f"{test_name.lower()}-"

name = f'{test_name}scylla-per-server-metrics-nemesis'
+ title = 'Scylla Per Server Metrics Nemesis'
path = 'dashboard/db/{dashboard_name}-{version}'
resolution = '1920px*8000px'
panels = [Panel("Total Requests"),
@@ -181,6 +179,7 @@ class ServerMetricsNemesisDashboard(Dashboard):

class AlternatorDashboard(Dashboard):
name = 'alternator'
+ title = 'Alternator'
path = 'd/alternator-{version}/{dashboard_name}'
resolution = '1920px*4000px'
panels = [Panel("Total Actions"),
Reply all
Reply to author
Forward
0 new messages