-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test(robot): add test case Check If Nodes Are Under Memory Pressure After Cluster Restart #2285
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
*** Settings *** | ||
Documentation Metrics Keywords | ||
|
||
Library ../libs/keywords/metrics_keywords.py | ||
|
||
*** Keywords *** | ||
Check if nodes are under memory pressure | ||
${worker_nodes} = get_worker_nodes | ||
FOR ${worker_node} IN @{worker_nodes} | ||
get_node_memory_usage_in_percentage ${worker_node} | ||
check_if_node_under_memory_pressure ${worker_node} | ||
END |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,49 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from node import Node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from metrics.metrics import get_node_metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from utility.utility import get_retry_count_and_interval | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from utility.utility import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class metrics_keywords: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def __init__(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.node = Node() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
retry_count, retry_interval = get_retry_count_and_interval() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_total_memory_in_mi(self, node_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_memory = self.node.get_node_total_memory(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if "Ki" in total_memory: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_memory = int(total_memory.replace("Ki", "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_memory = total_memory / 1024 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elif "Mi" in total_memory: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_memory = int(total_memory.replace("Mi", "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f'Got node {node_name} total memory: {total_memory} Mi') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return total_memory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_memory_usage_in_mi(self, node_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage = get_node_metrics(node_name, 'memory') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if "Ki" in memory_usage: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage = int(memory_usage.replace("Ki", "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage = memory_usage / 1024 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elif "Mi" in memory_usage: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage = int(memory_usage.replace("Mi", "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f'Got node {node_name} memory usage: {memory_usage} Mi') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return memory_usage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_memory_usage_in_percentage(self, node_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage_in_mi = self.get_node_memory_usage_in_mi(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_memory_in_mi = self.get_node_total_memory_in_mi(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
memory_usage_in_percentage = memory_usage_in_mi / total_memory_in_mi * 100 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f'Got node {node_name} memory usage: {memory_usage_in_percentage} %') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return memory_usage_in_percentage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+15
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Refactor memory conversion logic to reduce code duplication. The memory conversion logic is duplicated in both + def _convert_to_mi(self, memory_value: str) -> float:
+ """Convert memory value to MiB.
+
+ Args:
+ memory_value: Memory value with unit (e.g., "1024Ki", "512Mi")
+
+ Returns:
+ float: Memory value in MiB
+ """
+ if "Ki" in memory_value:
+ return int(memory_value.replace("Ki", "")) / 1024
+ elif "Mi" in memory_value:
+ return int(memory_value.replace("Mi", ""))
+ return float(memory_value) # Assume MiB if no unit
def get_node_total_memory_in_mi(self, node_name):
total_memory = self.node.get_node_total_memory(node_name)
- if "Ki" in total_memory:
- total_memory = int(total_memory.replace("Ki", ""))
- total_memory = total_memory / 1024
- elif "Mi" in total_memory:
- total_memory = int(total_memory.replace("Mi", ""))
+ total_memory = self._convert_to_mi(total_memory)
logging(f'Got node {node_name} total memory: {total_memory} Mi')
return total_memory
def get_node_memory_usage_in_mi(self, node_name):
memory_usage = get_node_metrics(node_name, 'memory')
- if "Ki" in memory_usage:
- memory_usage = int(memory_usage.replace("Ki", ""))
- memory_usage = memory_usage / 1024
- elif "Mi" in memory_usage:
- memory_usage = int(memory_usage.replace("Mi", ""))
+ memory_usage = self._convert_to_mi(memory_usage)
logging(f'Got node {node_name} memory usage: {memory_usage} Mi')
return memory_usage 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def check_if_node_under_memory_pressure(self, node_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f"Checking if node {node_name} is under memory pressure") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
condition_status = self.node.get_node_condition(node_name, "MemoryPressure") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if condition_status == "True": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f"Node {node_name} is under memory pressure") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(self.retry_count) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert False, f"Node {node_name} is under memory pressure" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+42
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling in check_if_node_under_memory_pressure. Replace def check_if_node_under_memory_pressure(self, node_name):
logging(f"Checking if node {node_name} is under memory pressure")
condition_status = self.node.get_node_condition(node_name, "MemoryPressure")
if condition_status == "True":
logging(f"Node {node_name} is under memory pressure")
- time.sleep(self.retry_count)
- assert False, f"Node {node_name} is under memory pressure"
+ logging(f"Waiting {self.retry_interval}s before failing...")
+ time.sleep(self.retry_interval)
+ raise AssertionError(f"Node {node_name} is under memory pressure")
🧰 Tools🪛 Ruff (0.8.2)48-48: Do not Replace (B011) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,22 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from kubernetes import client | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from kubernetes.client.rest import ApiException | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from utility.utility import get_retry_count_and_interval | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from utility.utility import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_metrics(node_name, metrics_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
retry_count, retry_interval = get_retry_count_and_interval() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for i in range(retry_count): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
api = client.CustomObjectsApi() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
node_metrics = api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for node in node_metrics['items']: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if node_name == node['metadata']['name']: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f"Got node {node_name} metrics {metrics_name} = {node['usage'][metrics_name]}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return node['usage'][metrics_name] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
except ApiException as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f"Failed to get node {node_name} metrics {metrics_name}: {e}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(retry_interval) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert False, f"Failed to get node {node_name} metrics {metrics_name}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+9
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add type hints, docstring and improve error handling. The function implementation could be improved in several ways:
-def get_node_metrics(node_name, metrics_name):
+def get_node_metrics(node_name: str, metrics_name: str) -> str:
+ """Retrieve metrics for a specified Kubernetes node.
+
+ Args:
+ node_name: Name of the node to retrieve metrics for
+ metrics_name: Type of metrics to retrieve (e.g., 'memory', 'cpu')
+
+ Returns:
+ str: The metrics value for the specified node and metrics type
+
+ Raises:
+ AssertionError: If unable to retrieve metrics after retries
+ """
+ if metrics_name not in ['memory', 'cpu']:
+ raise ValueError(f"Unsupported metrics type: {metrics_name}")
retry_count, retry_interval = get_retry_count_and_interval()
- for i in range(retry_count):
+ for _i in range(retry_count):
api = client.CustomObjectsApi()
try:
node_metrics = api.list_cluster_custom_object("metrics.k8s.io", "v1beta1", "nodes")
for node in node_metrics['items']:
if node_name == node['metadata']['name']:
logging(f"Got node {node_name} metrics {metrics_name} = {node['usage'][metrics_name]}")
return node['usage'][metrics_name]
except ApiException as e:
logging(f"Failed to get node {node_name} metrics {metrics_name}: {e}")
time.sleep(retry_interval)
- assert False, f"Failed to get node {node_name} metrics {metrics_name}"
+ raise AssertionError(f"Failed to get node {node_name} metrics {metrics_name}") 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.8.2)11-11: Loop control variable Rename unused (B007) 22-22: Do not Replace (B011) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -120,6 +120,18 @@ def get_node_cpu_cores(self, node_name): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
node = self.get_node_by_name(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return node.status.capacity['cpu'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_total_memory(self, node_name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
node = self.get_node_by_name(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return node.status.capacity['memory'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
def get_node_condition(self, node_name, condition_type): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
node = self.get_node_by_name(node_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
for condition in node.status.conditions: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
if condition.type == condition_type: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
logging(f"Got node {node_name} condition {condition_type}: {condition}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
return condition.status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
assert False, f"Failed to get node {node_name} condition {condition_type}: {node}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+127
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling and add documentation.
- def get_node_condition(self, node_name, condition_type):
+ def get_node_condition(self, node_name: str, condition_type: str) -> str:
+ """Get the status of a specific condition type for a node.
+
+ Args:
+ node_name: Name of the node to check.
+ condition_type: Type of condition to retrieve (e.g., 'Ready', 'MemoryPressure').
+
+ Returns:
+ str: The status of the condition ('True', 'False', or 'Unknown').
+
+ Raises:
+ RuntimeError: If the condition type is not found for the node.
+ """
node = self.get_node_by_name(node_name)
for condition in node.status.conditions:
if condition.type == condition_type:
logging(f"Got node {node_name} condition {condition_type}: {condition}")
return condition.status
- assert False, f"Failed to get node {node_name} condition {condition_type}: {node}"
+ raise RuntimeError(f"Failed to get node {node_name} condition {condition_type}: {node}") 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.8.2)133-133: Do not Replace (B011) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
def list_node_names_by_volumes(self, volume_names): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
volume_keywords = BuiltIn().get_library_instance('volume_keywords') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
volume_nodes = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialize retry_interval in constructor.
The
retry_interval
value is used incheck_if_node_under_memory_pressure
but not initialized in the constructor.📝 Committable suggestion