krkn-lib 5.1.12__tar.gz → 6.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/PKG-INFO +5 -8
  2. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/pyproject.toml +4 -3
  3. krkn_lib-6.0.0/src/krkn_lib/aws_tests/__init__.py +1 -0
  4. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/elastic/krkn_elastic.py +3 -1
  5. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/krkn_kubernetes.py +408 -20
  6. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/pod_monitor/__init__.py +1 -2
  7. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/pod_monitor/pod_monitor.py +146 -56
  8. krkn_lib-6.0.0/src/krkn_lib/k8s/templates/snapshot.j2 +10 -0
  9. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/elastic/models.py +2 -1
  10. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/k8s/models.py +1 -1
  11. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/pod_monitor/models.py +2 -2
  12. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/ocp/krkn_openshift.py +4 -4
  13. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/prometheus/krkn_prometheus.py +1 -1
  14. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/telemetry/k8s/krkn_telemetry_kubernetes.py +1 -1
  15. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/telemetry/ocp/krkn_telemetry_openshift.py +1 -1
  16. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/base_test.py +4 -4
  17. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_elastic_models.py +2 -4
  18. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_check.py +3 -2
  19. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_create.py +5 -3
  20. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_delete.py +3 -2
  21. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_get.py +5 -4
  22. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_misc.py +3 -3
  23. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_pods_monitor_models.py +3 -4
  24. krkn_lib-6.0.0/src/krkn_lib/tests/test_krkn_kubernetes_virt.py +735 -0
  25. krkn_lib-6.0.0/src/krkn_lib/tests/test_krkn_openshift.py +646 -0
  26. krkn_lib-6.0.0/src/krkn_lib/tests/test_krkn_telemetry_kubernetes.py +848 -0
  27. krkn_lib-6.0.0/src/krkn_lib/tests/test_safe_logger.py +496 -0
  28. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_utils.py +4 -5
  29. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/utils/functions.py +4 -3
  30. krkn_lib-6.0.0/src/krkn_lib/version/version.py +6 -0
  31. krkn_lib-5.1.12/src/krkn_lib/aws_tests/__init__.py +0 -1
  32. krkn_lib-5.1.12/src/krkn_lib/tests/test_krkn_openshift.py +0 -123
  33. krkn_lib-5.1.12/src/krkn_lib/version/version.py +0 -3
  34. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/LICENSE +0 -0
  35. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/README.md +0 -0
  36. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/__init__.py +0 -0
  37. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/aws_tests/test_krkn_telemetry_kubernetes.py +0 -0
  38. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/aws_tests/test_krkn_telemetry_openshift.py +0 -0
  39. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/elastic/__init__.py +0 -0
  40. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/__init__.py +0 -0
  41. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/templates/hog_pod.j2 +0 -0
  42. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/templates/node_exec_pod.j2 +0 -0
  43. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/templates/service_hijacking_config_map.j2 +0 -0
  44. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/templates/service_hijacking_pod.j2 +0 -0
  45. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/k8s/templates/syn_flood_pod.j2 +0 -0
  46. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/__init__.py +0 -0
  47. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/elastic/__init__.py +0 -0
  48. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/k8s/__init__.py +0 -0
  49. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/krkn/__init__.py +0 -0
  50. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/krkn/models.py +0 -0
  51. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/pod_monitor/__init__.py +0 -0
  52. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/telemetry/__init__.py +0 -0
  53. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/models/telemetry/models.py +0 -0
  54. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/ocp/__init__.py +0 -0
  55. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/prometheus/__init__.py +0 -0
  56. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/telemetry/__init__.py +0 -0
  57. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/telemetry/k8s/__init__.py +0 -0
  58. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/telemetry/ocp/__init__.py +0 -0
  59. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/__init__.py +0 -0
  60. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_elastic.py +0 -0
  61. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_exec.py +0 -0
  62. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_list.py +0 -0
  63. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_models.py +1 -1
  64. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_kubernetes_pods_monitor.py +0 -0
  65. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_prometheus.py +0 -0
  66. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_krkn_telemetry_models.py +0 -0
  67. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/tests/test_version.py +0 -0
  68. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/utils/__init__.py +0 -0
  69. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/utils/safe_logger.py +0 -0
  70. {krkn_lib-5.1.12 → krkn_lib-6.0.0}/src/krkn_lib/version/__init__.py +0 -0
@@ -1,19 +1,16 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: krkn-lib
3
- Version: 5.1.12
3
+ Version: 6.0.0
4
4
  Summary: Foundation library for Kraken
5
+ Home-page: https://github.com/redhat-chaos/krkn
5
6
  License: Apache-2.0
6
- License-File: LICENSE
7
7
  Author: Red Hat Chaos Team
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.11,<4.0
9
9
  Classifier: License :: OSI Approved :: Apache Software License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
11
  Classifier: Programming Language :: Python :: 3.11
14
12
  Classifier: Programming Language :: Python :: 3.12
15
13
  Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
17
14
  Requires-Dist: PyYAML (==6.0.1)
18
15
  Requires-Dist: base64io (>=1.0.3,<2.0.0)
19
16
  Requires-Dist: coverage (>=7.6.12,<8.0.0)
@@ -21,6 +18,7 @@ Requires-Dist: cython (==3.0)
21
18
  Requires-Dist: deprecation (==2.1.0)
22
19
  Requires-Dist: elasticsearch (==7.13.4)
23
20
  Requires-Dist: elasticsearch-dsl (==7.4.1)
21
+ Requires-Dist: importlib-metadata (>=8.7.0,<9.0.0)
24
22
  Requires-Dist: kubeconfig (>=1.1.1,<2.0.0)
25
23
  Requires-Dist: kubernetes (==34.1.0)
26
24
  Requires-Dist: numpy (==1.26.4)
@@ -31,7 +29,6 @@ Requires-Dist: sphinx-rtd-theme (>=1.2.2,<2.0.0)
31
29
  Requires-Dist: sphinxnotes-markdown-builder (>=0.5.6,<0.6.0)
32
30
  Requires-Dist: tzlocal (==5.1)
33
31
  Requires-Dist: wheel (>=0.42.0,<0.43.0)
34
- Project-URL: Homepage, https://github.com/redhat-chaos/krkn
35
32
  Description-Content-Type: text/markdown
36
33
 
37
34
  ![action](https://github.com/krkn-chaos/krkn-lib/actions/workflows/build.yaml/badge.svg)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "krkn-lib"
3
- version = "5.1.12"
3
+ version = "6.0.0"
4
4
  description = "Foundation library for Kraken"
5
5
  authors = ["Red Hat Chaos Team"]
6
6
  license = "Apache-2.0"
@@ -9,7 +9,7 @@ homepage = "https://github.com/redhat-chaos/krkn"
9
9
  #packages = [{include= "src/krkn_lib"}]
10
10
 
11
11
  [tool.poetry.dependencies]
12
- python = "^3.9"
12
+ python = "^3.11"
13
13
  kubernetes ="34.1.0"
14
14
  sphinxnotes-markdown-builder="^0.5.6"
15
15
  requests="^2.29.0"
@@ -27,6 +27,7 @@ cython = "3.0"
27
27
  numpy= "1.26.4"
28
28
  deprecation="2.1.0"
29
29
  coverage="^7.6.12"
30
+ importlib-metadata = "^8.7.0"
30
31
 
31
32
  [tool.poetry.group.test.dependencies]
32
33
  jinja2 = "^3.1.2"
@@ -34,7 +35,7 @@ boto3 = "^1.28.12"
34
35
 
35
36
  [tool.black]
36
37
  line-length = 79
37
- target-version = ['py37', 'py38']
38
+ target-version = ['py311', 'py312']
38
39
  include = '\.pyi?$'
39
40
  exclude = '''
40
41
  /(
@@ -0,0 +1 @@
1
+ from ..tests.base_test import * # NOQA
@@ -53,7 +53,9 @@ class KrknElastic:
53
53
  ssl_show_warn=False,
54
54
  )
55
55
  except Exception as e:
56
- self.safe_logger.error("Failed to initalize elasticsearch: %s" % e)
56
+ self.safe_logger.error(
57
+ "Failed to initialize elasticsearch: %s" % e
58
+ )
57
59
  raise e
58
60
 
59
61
  def upload_data_to_elasticsearch(self, item: dict, index: str = "") -> int:
@@ -159,17 +159,23 @@ class KrknKubernetes:
159
159
  os.environ["HTTP_PROXY"] = http_proxy
160
160
  self.client_config.proxy = http_proxy
161
161
  proxy_auth = urlparse(http_proxy)
162
- auth_string = proxy_auth.username + ":" + proxy_auth.password
163
- self.client_config.proxy_headers = urllib3.util.make_headers(
164
- proxy_basic_auth=auth_string
165
- )
162
+ if (
163
+ proxy_auth.username is not None
164
+ and proxy_auth.password is not None
165
+ ):
166
+ auth_string = (
167
+ proxy_auth.username + ":" + proxy_auth.password
168
+ )
169
+ self.client_config.proxy_headers = (
170
+ urllib3.util.make_headers(proxy_basic_auth=auth_string)
171
+ )
166
172
 
167
173
  client.Configuration.set_default(self.client_config)
168
174
  self.watch_resource = watch.Watch()
169
175
  # Get the logger for the kubernetes client
170
- kubernetes_logger = logging.getLogger('kubernetes')
176
+ kubernetes_logger = logging.getLogger("kubernetes")
171
177
 
172
- # Set the logging level to a higher level than DEBUG,
178
+ # Set the logging level to a higher level than DEBUG,
173
179
  # such as INFO, WARNING, or ERROR
174
180
  # This will effectively disable DEBUG level messages.
175
181
  kubernetes_logger.setLevel(logging.INFO)
@@ -185,7 +191,7 @@ class KrknKubernetes:
185
191
  on other distributions
186
192
 
187
193
  *** IT MUST BE CONSIDERED A PRIVATE METHOD (CANNOT USE
188
- *** DOUBLE UNDERSCORE TO MANTAIN IT VISIBLE ON SUB-CLASSES)
194
+ *** DOUBLE UNDERSCORE TO MAINTAIN IT VISIBLE ON SUB-CLASSES)
189
195
  *** USED IN KrknKubernetes and KrknOpenshift TO AUTODETECT
190
196
  *** THE CLUSTER TYPE
191
197
 
@@ -237,7 +243,10 @@ class KrknKubernetes:
237
243
  minor_version = api_response.minor
238
244
  return major_version + "." + minor_version
239
245
  except ApiException as e:
240
- print("Exception when calling VersionApi->get_code: %s\n" % e)
246
+ logging.error(
247
+ "Exception when calling VersionApi->get_code: %s", str(e)
248
+ )
249
+ raise e
241
250
 
242
251
  def get_host(self) -> str:
243
252
  """
@@ -874,7 +883,7 @@ class KrknKubernetes:
874
883
  services.append(serv.metadata.name)
875
884
  return services
876
885
 
877
- # Outputs a json blob with informataion about all pods in a given namespace
886
+ # Outputs a json blob with information about all pods in a given namespace
878
887
  def get_all_pod_info(
879
888
  self,
880
889
  namespace: str = "default",
@@ -978,7 +987,7 @@ class KrknKubernetes:
978
987
  :param command: command parameters list or full command string
979
988
  if the command must be piped to `bash -c`
980
989
  (in that case `base_command` parameter
981
- must is omitted`)
990
+ must be omitted`)
982
991
  :param pod_name: pod where the command must be executed
983
992
  :param namespace: namespace of the pod
984
993
  :param container: container where the command
@@ -1038,7 +1047,7 @@ class KrknKubernetes:
1038
1047
  :param command: command parameters list or full command string
1039
1048
  if the command must be piped to `bash -c`
1040
1049
  (in that case `base_command` parameter
1041
- must is omitted`)
1050
+ must be omitted`)
1042
1051
  :param pod_name: pod where the command must be executed
1043
1052
  :param namespace: namespace of the pod
1044
1053
  :param container: container where the command
@@ -1093,7 +1102,7 @@ class KrknKubernetes:
1093
1102
  )
1094
1103
  except Exception as e:
1095
1104
  raise e
1096
- # apparently stream API doesn't rise an Exception
1105
+ # apparently stream API doesn't raise an Exception
1097
1106
  # if the command fails to be executed
1098
1107
 
1099
1108
  if "OCI runtime exec failed" in ret:
@@ -1388,6 +1397,311 @@ class KrknKubernetes:
1388
1397
  str(e),
1389
1398
  )
1390
1399
 
1400
+ def get_vm(self, name: str, namespace: str) -> Optional[Dict]:
1401
+ """
1402
+ Get a Virtual Machine by name and namespace.
1403
+
1404
+ :param name: Name of the VM to retrieve
1405
+ :param namespace: Namespace of the VM
1406
+ :return: The VM object if found, None otherwise
1407
+ """
1408
+ try:
1409
+ vm = self.custom_object_client.get_namespaced_custom_object(
1410
+ group="kubevirt.io",
1411
+ version="v1",
1412
+ namespace=namespace,
1413
+ plural="virtualmachines",
1414
+ name=name,
1415
+ )
1416
+ return vm
1417
+ except ApiException as e:
1418
+ if e.status == 404:
1419
+ logging.warning(
1420
+ f"VM {name} not found in namespace {namespace}"
1421
+ )
1422
+ return None
1423
+ else:
1424
+ logging.error(f"Error getting VM {name}: {e}")
1425
+ raise
1426
+ except Exception as e:
1427
+ logging.error(f"Unexpected error getting VM {name}: {e}")
1428
+ raise
1429
+
1430
+ def get_vmi(self, name: str, namespace: str) -> Optional[Dict]:
1431
+ """
1432
+ Get a Virtual Machine Instance by name and namespace.
1433
+
1434
+ :param name: Name of the VMI to retrieve
1435
+ :param namespace: Namespace of the VMI
1436
+ :return: The VMI object if found, None otherwise
1437
+ """
1438
+ try:
1439
+ vmi = self.custom_object_client.get_namespaced_custom_object(
1440
+ group="kubevirt.io",
1441
+ version="v1",
1442
+ namespace=namespace,
1443
+ plural="virtualmachineinstances",
1444
+ name=name,
1445
+ )
1446
+ return vmi
1447
+ except ApiException as e:
1448
+ if e.status == 404:
1449
+ logging.warning(
1450
+ f"VMI {name} not found in namespace {namespace}"
1451
+ )
1452
+ return None
1453
+ else:
1454
+ logging.error(f"Error getting VMI {name}: {e}")
1455
+ raise
1456
+ except Exception as e:
1457
+ logging.error(f"Unexpected error getting VMI {name}: {e}")
1458
+ raise
1459
+
1460
+ def get_vmis(self, regex_name: str, namespace: str) -> Optional[Dict]:
1461
+ """
1462
+ Get a Virtual Machine Instance by name and namespace.
1463
+
1464
+ :param name: Name of the VMI to retrieve
1465
+ :param namespace: Namespace of the VMI
1466
+ :return: The VMI object if found, None otherwise
1467
+ """
1468
+ try:
1469
+ vmis_list = []
1470
+ namespaces = self.list_namespaces_by_regex(namespace)
1471
+ for namespace in namespaces:
1472
+ vmis = self.custom_object_client.list_namespaced_custom_object(
1473
+ group="kubevirt.io",
1474
+ version="v1",
1475
+ namespace=namespace,
1476
+ plural="virtualmachineinstances",
1477
+ )
1478
+
1479
+ for vmi in vmis.get("items"):
1480
+ vmi_name = vmi.get("metadata", {}).get("name")
1481
+ match = re.match(regex_name, vmi_name)
1482
+ if match:
1483
+ vmis_list.append(vmi)
1484
+ except ApiException as e:
1485
+ if e.status == 404:
1486
+ logging.warning(
1487
+ f"VMI {regex_name} not found in namespace {namespace}"
1488
+ )
1489
+ return []
1490
+ else:
1491
+ logging.error(f"Error getting VMI {regex_name}: {e}")
1492
+ raise
1493
+ except Exception as e:
1494
+ logging.error(f"Unexpected error getting VMI {regex_name}: {e}")
1495
+ raise
1496
+ return vmis_list
1497
+
1498
+ def create_vmi(
1499
+ self, name: str, namespace: str, vm_name: str, vmi_body: dict
1500
+ ) -> Optional[Dict]:
1501
+ """
1502
+ Create a Virtual Machine Instance by name and namespace.
1503
+
1504
+ :param name: Name of the VMI to create
1505
+ :param namespace: Namespace of the VMI
1506
+ :param vm_name: Name of the Virtual Machine to create the VMI from
1507
+ :return: The VMI object if created, None otherwise
1508
+ """
1509
+ try:
1510
+ vmi = self.custom_object_client.create_namespaced_custom_object(
1511
+ group="kubevirt.io",
1512
+ version="v1",
1513
+ namespace=namespace,
1514
+ plural="virtualmachineinstances",
1515
+ body=vmi_body,
1516
+ )
1517
+ return vmi
1518
+ except ApiException as e:
1519
+ if e.status == 404:
1520
+ logging.warning(
1521
+ f"VMI {name} not found in namespace {namespace}"
1522
+ )
1523
+ return None
1524
+ else:
1525
+ logging.error(f"Error creating VMI {name}: {e}")
1526
+ raise
1527
+ except Exception as e:
1528
+ logging.error(f"Unexpected error creating VMI {name}: {e}")
1529
+ raise
1530
+
1531
+ def patch_vm(
1532
+ self, name: str, namespace: str, vm_body: dict
1533
+ ) -> Optional[Dict]:
1534
+ """
1535
+ Patch a Virtual Machine by name and namespace.
1536
+
1537
+ :param name: Name of the VM to patch
1538
+ :param namespace: Namespace of the VM
1539
+ :param vm_body: Body of the VM to patch
1540
+ :return: The VM object if patched, None otherwise
1541
+ """
1542
+ try:
1543
+ vmi = self.custom_object_client.patch_namespaced_custom_object(
1544
+ group="kubevirt.io",
1545
+ version="v1",
1546
+ namespace=namespace,
1547
+ plural="virtualmachines",
1548
+ name=name,
1549
+ body=vm_body,
1550
+ )
1551
+ return vmi
1552
+ except ApiException as e:
1553
+ if e.status == 404:
1554
+ logging.warning(
1555
+ f"VM {name} not found in namespace {namespace}"
1556
+ )
1557
+ return None
1558
+ else:
1559
+ logging.error(f"Error patching VM {name}: {e}")
1560
+ raise
1561
+ except Exception as e:
1562
+ logging.error(f"Unexpected error patching VM {name}: {e}")
1563
+ raise
1564
+
1565
+ def patch_vmi(
1566
+ self, name: str, namespace: str, vmi_body: dict
1567
+ ) -> Optional[Dict]:
1568
+ """
1569
+ Patch a Virtual Machine Instance by name and namespace.
1570
+
1571
+ :param name: Name of the VMI to patch
1572
+ :param namespace: Namespace of the VMI
1573
+ :param vmi_body: Body of the VMI to patch
1574
+ :return: The VMI object if patched, None otherwise
1575
+ """
1576
+ try:
1577
+ vmi = self.custom_object_client.patch_namespaced_custom_object(
1578
+ group="kubevirt.io",
1579
+ version="v1",
1580
+ namespace=namespace,
1581
+ plural="virtualmachineinstances",
1582
+ name=name,
1583
+ body=vmi_body,
1584
+ )
1585
+ return vmi
1586
+ except ApiException as e:
1587
+ if e.status == 404:
1588
+ logging.warning(
1589
+ f"VMI {name} not found in namespace {namespace}"
1590
+ )
1591
+ return None
1592
+ else:
1593
+ logging.error(f"Error patching VMI {name}: {e}")
1594
+ raise
1595
+ except Exception as e:
1596
+ logging.error(f"Unexpected error patching VMI {name}: {e}")
1597
+ raise
1598
+
1599
+ def get_vms(self, regex_name: str, namespace: str) -> Optional[Dict]:
1600
+ """
1601
+ Get a Virtual Machine by name and namespace.
1602
+
1603
+ :param name: Name of the VM to retrieve
1604
+ :param namespace: Namespace of the VM
1605
+ :return: The VM object if found, None otherwise
1606
+ """
1607
+ try:
1608
+ vms_list = []
1609
+ namespaces = self.list_namespaces_by_regex(namespace)
1610
+ for namespace in namespaces:
1611
+ vms = self.custom_object_client.list_namespaced_custom_object(
1612
+ group="kubevirt.io",
1613
+ version="v1",
1614
+ namespace=namespace,
1615
+ plural="virtualmachines",
1616
+ )
1617
+
1618
+ for vm in vms.get("items"):
1619
+ vm_name = vm.get("metadata", {}).get("name")
1620
+ match = re.match(regex_name, vm_name)
1621
+ if match:
1622
+ vms_list.append(vm)
1623
+ return vms_list
1624
+ except ApiException as e:
1625
+ if e.status == 404:
1626
+ logging.warning(
1627
+ f"VM {regex_name} not found in namespace {namespace}"
1628
+ )
1629
+ return []
1630
+ else:
1631
+ logging.error(f"Error getting VM {regex_name}: {e}")
1632
+ raise
1633
+ except Exception as e:
1634
+ logging.error(f"Unexpected error getting VM {regex_name}: {e}")
1635
+ raise
1636
+
1637
+ def get_snapshot(self, name: str, namespace: str) -> Optional[Dict]:
1638
+ """
1639
+ Get a Snapshot by name and namespace.
1640
+
1641
+ :param name: Name of the Snapshot to retrieve
1642
+ :param namespace: Namespace of the Snapshot
1643
+ :return: The Snapshot object if found, None otherwise
1644
+ """
1645
+ try:
1646
+ snapshot = self.custom_object_client.get_namespaced_custom_object(
1647
+ group="snapshot.kubevirt.io",
1648
+ version="v1beta1",
1649
+ namespace=namespace,
1650
+ plural="virtualmachinesnapshots",
1651
+ name=name,
1652
+ )
1653
+ return snapshot
1654
+ except ApiException as e:
1655
+ if e.status == 404:
1656
+ logging.warning(
1657
+ f"SnapShot {name} not found in namespace {namespace}"
1658
+ )
1659
+ return None
1660
+ else:
1661
+ logging.error(f"Error getting snapshot {name}: {e}")
1662
+ raise
1663
+ except Exception as e:
1664
+ logging.error(f"Unexpected error getting snapshot {name}: {e}")
1665
+ raise
1666
+
1667
+ def create_snapshot(
1668
+ self, name: str, namespace: str, vm_name: str
1669
+ ) -> Optional[Dict]:
1670
+ """
1671
+ Create a Snapshot by name and namespace.
1672
+
1673
+ :param name: Name of the Snapshot to create
1674
+ :param namespace: Namespace of the Snapshot
1675
+ :param vm_name: Name of the Virtual Machine to create the Snapshot from
1676
+ :return: The Snapshot object if created, None otherwise
1677
+ """
1678
+ try:
1679
+ file_loader = PackageLoader("krkn_lib.k8s", "templates")
1680
+ env = Environment(loader=file_loader, autoescape=True)
1681
+ snapshot_template = env.get_template("snapshot.j2")
1682
+ ss_body = yaml.safe_load(
1683
+ snapshot_template.render(
1684
+ name=name, namespace=namespace, vm_name=vm_name
1685
+ )
1686
+ )
1687
+ snapshot = (
1688
+ self.custom_object_client.create_namespaced_custom_object(
1689
+ group="snapshot.kubevirt.io",
1690
+ version="v1beta1",
1691
+ namespace=namespace,
1692
+ plural="virtualmachinesnapshots",
1693
+ body=ss_body,
1694
+ )
1695
+ )
1696
+ return snapshot
1697
+ except ApiException as e:
1698
+
1699
+ logging.error(f"Error creating Snapshot {name}: {e}")
1700
+ raise
1701
+ except Exception as e:
1702
+ logging.error(f"Unexpected error creating Snapshot {name}: {e}")
1703
+ raise
1704
+
1391
1705
  def get_job_status(
1392
1706
  self, name: str, namespace: str = "default"
1393
1707
  ) -> client.V1Job:
@@ -1410,6 +1724,80 @@ class KrknKubernetes:
1410
1724
  )
1411
1725
  raise
1412
1726
 
1727
+ def delete_vm(self, name: str, namespace: str) -> Optional[Dict]:
1728
+ """
1729
+ Delete a Virtual Machine by name and namespace.
1730
+
1731
+ :param name: Name of the VM to delete
1732
+ :param namespace: Namespace of the VM
1733
+ :return: The VM object if found, None otherwise
1734
+ """
1735
+ try:
1736
+ return self.custom_object_client.delete_namespaced_custom_object(
1737
+ group="kubevirt.io",
1738
+ version="v1",
1739
+ namespace=namespace,
1740
+ plural="virtualmachines",
1741
+ name=name,
1742
+ )
1743
+ except ApiException as e:
1744
+ if e.status == 404:
1745
+ logging.warning(
1746
+ f"VM {name} not found in namespace {namespace}"
1747
+ )
1748
+ return None
1749
+ else:
1750
+ logging.error(f"Error deleting VM {name}: {e}")
1751
+ raise
1752
+ except Exception as e:
1753
+ logging.error(f"Error deleting VM {name}: {e}")
1754
+ raise
1755
+
1756
+ def delete_vmi(self, vm_name: str, namespace: str):
1757
+ """
1758
+ Delete a Virtual Machine Instance to simulate a VM outage.
1759
+
1760
+ :param vm_name: Name of the VMI to delete
1761
+ :param namespace: Namespace of the VMI
1762
+ :return: 0 for success, 1 for failure
1763
+ """
1764
+ logging.info(
1765
+ f"Injecting chaos: Deleting VMI {vm_name} in namespace {namespace}"
1766
+ )
1767
+ try:
1768
+ self.custom_object_client.delete_namespaced_custom_object(
1769
+ group="kubevirt.io",
1770
+ version="v1",
1771
+ namespace=namespace,
1772
+ plural="virtualmachineinstances",
1773
+ name=vm_name,
1774
+ )
1775
+ except ApiException as e:
1776
+ if e.status == 404:
1777
+ logging.warning(f"VMI {vm_name} not found during deletion")
1778
+ return 1
1779
+ else:
1780
+ logging.error(f"API error during VMI deletion: {e}")
1781
+ return 1
1782
+
1783
+ def delete_snapshot(self, snapshot_name: str, namespace: str):
1784
+ """Helper method to delete any snapshot created by the scenario."""
1785
+ logging.info(f"Deleting snapshot '{snapshot_name}'...")
1786
+ try:
1787
+ self.custom_object_client.delete_namespaced_custom_object(
1788
+ group="snapshot.kubevirt.io",
1789
+ version="v1beta1",
1790
+ namespace=namespace,
1791
+ plural="virtualmachinesnapshots",
1792
+ name=snapshot_name,
1793
+ )
1794
+ logging.info(f"Snapshot '{snapshot_name}' deleted successfully.")
1795
+ except Exception as e:
1796
+ logging.warning(
1797
+ "Failed to delete snapshot, "
1798
+ f"might have been already deleted: {e}"
1799
+ )
1800
+
1413
1801
  def monitor_nodes(
1414
1802
  self,
1415
1803
  ) -> (bool, list[str]):
@@ -1432,6 +1820,7 @@ class KrknKubernetes:
1432
1820
  str(e),
1433
1821
  )
1434
1822
  raise e
1823
+ node_ready_status = "False"
1435
1824
  for condition in node_info.status.conditions:
1436
1825
  if condition.type == "KernelDeadlock":
1437
1826
  node_kerneldeadlock_status = condition.status
@@ -2005,7 +2394,7 @@ class KrknKubernetes:
2005
2394
  )
2006
2395
 
2007
2396
  path = f"/api/{api_version}/{resource.name}"
2008
- (data) = self.api_client.call_api(
2397
+ data = self.api_client.call_api(
2009
2398
  path,
2010
2399
  "GET",
2011
2400
  path_params,
@@ -2168,7 +2557,6 @@ class KrknKubernetes:
2168
2557
  else:
2169
2558
  node_info.nodes_type = "unknown"
2170
2559
 
2171
- node_info.architecture = node.status.node_info.architecture
2172
2560
  node_info.architecture = node.status.node_info.architecture
2173
2561
  node_info.kernel_version = node.status.node_info.kernel_version
2174
2562
  node_info.kubelet_version = (
@@ -2255,7 +2643,7 @@ class KrknKubernetes:
2255
2643
  sequential number of the archive assigned to the worker
2256
2644
  and the extension tar.b64
2257
2645
  :param queue: the queue from which the sequential
2258
- number wil be popped
2646
+ number will be popped
2259
2647
  :param queue_size: total size of the queue
2260
2648
  :param downloaded_file_list: the list of
2261
2649
  archive number and local filename downloaded
@@ -2377,7 +2765,7 @@ class KrknKubernetes:
2377
2765
  where the temporary archive
2378
2766
  will be stored (will be deleted once the download
2379
2767
  terminates, must be writable
2380
- and must have enough space to temporarly store the archive)
2768
+ and must have enough space to temporarily store the archive)
2381
2769
  :param target_path: the path that will be archived
2382
2770
  and downloaded from the container
2383
2771
  :param archive_files_prefix: prefix string that will be added
@@ -2640,7 +3028,7 @@ class KrknKubernetes:
2640
3028
  ["application/json"]
2641
3029
  )
2642
3030
  try:
2643
- (data) = self.api_client.call_api(
3031
+ data = self.api_client.call_api(
2644
3032
  path,
2645
3033
  "POST",
2646
3034
  path_params,
@@ -2881,7 +3269,7 @@ class KrknKubernetes:
2881
3269
  default 5000
2882
3270
  :param port_name: the port name if the Service is pointing to
2883
3271
  a string name instead of a port number
2884
- :param stats_route: overrides the defautl route where the stats
3272
+ :param stats_route: overrides the default route where the stats
2885
3273
  action will be mapped, change it only if you have a /stats
2886
3274
  route in your test_plan
2887
3275
  :return: a structure containing all the infos of the
@@ -2946,7 +3334,7 @@ class KrknKubernetes:
2946
3334
 
2947
3335
  def service_exists(self, service_name: str, namespace: str) -> bool:
2948
3336
  """
2949
- Checks wheter a kubernetes Service exist or not
3337
+ Checks whether a kubernetes Service exist or not
2950
3338
  :param service_name: the name of the service to check
2951
3339
  :param namespace: the namespace where the service should exist
2952
3340
  :return: True if the service exists, False if not
@@ -3059,7 +3447,7 @@ class KrknKubernetes:
3059
3447
  ["application/json"]
3060
3448
  )
3061
3449
  path = f"/api/v1/nodes/{node_name}/proxy/stats/summary"
3062
- (data) = self.api_client.call_api(
3450
+ data = self.api_client.call_api(
3063
3451
  path,
3064
3452
  "GET",
3065
3453
  path_params,
@@ -1,10 +1,9 @@
1
1
  from .pod_monitor import (
2
2
  select_and_monitor_by_label,
3
- select_and_monitor_by_namespace_pattern_and_label,
4
3
  select_and_monitor_by_name_pattern_and_namespace_pattern,
4
+ select_and_monitor_by_namespace_pattern_and_label,
5
5
  )
6
6
 
7
-
8
7
  __all__ = [
9
8
  "select_and_monitor_by_label",
10
9
  "select_and_monitor_by_namespace_pattern_and_label",