krkn-lib 5.1.6__tar.gz → 5.1.8__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.
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/PKG-INFO +4 -2
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/pyproject.toml +1 -1
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/krkn_kubernetes.py +11 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/pod_monitor/pod_monitor.py +1 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/pod_monitor/models.py +8 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/base_test.py +2 -1
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_check.py +6 -2
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_list.py +33 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_pods_monitor.py +5 -1
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_utils.py +19 -1
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/utils/functions.py +17 -2
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/LICENSE +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/README.md +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/aws_tests/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/aws_tests/test_krkn_telemetry_kubernetes.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/aws_tests/test_krkn_telemetry_openshift.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/elastic/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/elastic/krkn_elastic.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/pod_monitor/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/hog_pod.j2 +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/node_exec_pod.j2 +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/service_hijacking_config_map.j2 +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/service_hijacking_pod.j2 +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/syn_flood_pod.j2 +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/elastic/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/elastic/models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/k8s/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/k8s/models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/krkn/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/krkn/models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/pod_monitor/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/telemetry/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/models/telemetry/models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/ocp/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/ocp/krkn_openshift.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/prometheus/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/prometheus/krkn_prometheus.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/telemetry/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/telemetry/k8s/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/telemetry/k8s/krkn_telemetry_kubernetes.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/telemetry/ocp/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/telemetry/ocp/krkn_telemetry_openshift.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_elastic.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_elastic_models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_create.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_delete.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_exec.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_get.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_misc.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_pods_monitor_models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_openshift.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_prometheus.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_telemetry_models.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_version.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/utils/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/utils/safe_logger.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/version/__init__.py +0 -0
- {krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/version/version.py +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: krkn-lib
|
|
3
|
-
Version: 5.1.
|
|
3
|
+
Version: 5.1.8
|
|
4
4
|
Summary: Foundation library for Kraken
|
|
5
5
|
License: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Author: Red Hat Chaos Team
|
|
7
8
|
Requires-Python: >=3.9,<4.0
|
|
8
9
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -12,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
17
|
Requires-Dist: PyYAML (==6.0.1)
|
|
16
18
|
Requires-Dist: base64io (>=1.0.3,<2.0.0)
|
|
17
19
|
Requires-Dist: coverage (>=7.6.12,<8.0.0)
|
|
@@ -528,6 +528,7 @@ class KrknKubernetes:
|
|
|
528
528
|
namespace: str,
|
|
529
529
|
label_selector: str = None,
|
|
530
530
|
field_selector: str = None,
|
|
531
|
+
exclude_label: str = None,
|
|
531
532
|
) -> list[str]:
|
|
532
533
|
"""
|
|
533
534
|
List pods in the given namespace
|
|
@@ -537,6 +538,8 @@ class KrknKubernetes:
|
|
|
537
538
|
(optional default `None`)
|
|
538
539
|
:param field_selector: filter results by config details
|
|
539
540
|
select only running pods by setting "status.phase=Running"
|
|
541
|
+
:param exclude_label: exclude pods matching this label
|
|
542
|
+
in format "key=value" (optional default `None`)
|
|
540
543
|
:return: a list of pod names
|
|
541
544
|
"""
|
|
542
545
|
pods = []
|
|
@@ -552,6 +555,14 @@ class KrknKubernetes:
|
|
|
552
555
|
raise e
|
|
553
556
|
for ret_list in ret:
|
|
554
557
|
for pod in ret_list.items:
|
|
558
|
+
# Skip pods with the exclude label if specified
|
|
559
|
+
if exclude_label and pod.metadata.labels:
|
|
560
|
+
exclude_key, exclude_value = exclude_label.split("=", 1)
|
|
561
|
+
if (
|
|
562
|
+
exclude_key in pod.metadata.labels
|
|
563
|
+
and pod.metadata.labels[exclude_key] == exclude_value
|
|
564
|
+
):
|
|
565
|
+
continue
|
|
555
566
|
pods.append(pod.metadata.name)
|
|
556
567
|
return pods
|
|
557
568
|
|
|
@@ -8,6 +8,7 @@ from krkn_lib.models.k8s import PodsStatus, AffectedPod
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class PodStatus(Enum):
|
|
11
|
+
UNDEFINED = 0
|
|
11
12
|
READY = 1
|
|
12
13
|
NOT_READY = 2
|
|
13
14
|
DELETION_SCHEDULED = 3
|
|
@@ -20,6 +21,7 @@ class PodEvent:
|
|
|
20
21
|
status: PodStatus
|
|
21
22
|
|
|
22
23
|
def __init__(self, timestamp: float = None):
|
|
24
|
+
self.status = PodStatus.UNDEFINED
|
|
23
25
|
if not timestamp:
|
|
24
26
|
self._timestamp = time.time()
|
|
25
27
|
else:
|
|
@@ -142,12 +144,18 @@ class PodsSnapshot:
|
|
|
142
144
|
)
|
|
143
145
|
)
|
|
144
146
|
else:
|
|
147
|
+
|
|
148
|
+
# pod stayed ready but was restarted
|
|
149
|
+
# or has a failed container
|
|
145
150
|
pods_status.recovered.append(
|
|
146
151
|
AffectedPod(
|
|
147
152
|
pod_name=pod.name,
|
|
148
153
|
namespace=pod.namespace,
|
|
149
154
|
pod_readiness_time=ready_status.timestamp
|
|
150
155
|
- status_change.timestamp,
|
|
156
|
+
pod_rescheduling_time=0,
|
|
157
|
+
total_recovery_time=ready_status.timestamp
|
|
158
|
+
- status_change.timestamp,
|
|
151
159
|
)
|
|
152
160
|
)
|
|
153
161
|
break
|
|
@@ -165,12 +165,13 @@ class BaseTest(unittest.TestCase):
|
|
|
165
165
|
|
|
166
166
|
def deploy_fake_kraken(
|
|
167
167
|
self,
|
|
168
|
+
name: str = "kraken-deployment",
|
|
168
169
|
namespace: str = "default",
|
|
169
170
|
random_label: str = None,
|
|
170
171
|
node_name: str = None,
|
|
171
172
|
):
|
|
172
173
|
template = self.template_to_pod(
|
|
173
|
-
|
|
174
|
+
name, namespace, random_label, node_name
|
|
174
175
|
)
|
|
175
176
|
self.apply_template(template)
|
|
176
177
|
|
|
@@ -82,7 +82,9 @@ class KrknKubernetesTestsCheck(BaseTest):
|
|
|
82
82
|
bad_namespace = "test-ns-" + self.get_random_string(10)
|
|
83
83
|
self.deploy_namespace(bad_namespace, [])
|
|
84
84
|
self.deploy_fake_kraken(
|
|
85
|
-
bad_namespace,
|
|
85
|
+
namespace=bad_namespace,
|
|
86
|
+
random_label=None,
|
|
87
|
+
node_name="do_not_exist",
|
|
86
88
|
)
|
|
87
89
|
status = self.lib_k8s.monitor_namespace(namespace=bad_namespace)
|
|
88
90
|
# sleeping for a while just in case
|
|
@@ -108,7 +110,9 @@ class KrknKubernetesTestsCheck(BaseTest):
|
|
|
108
110
|
bad_namespace = "test-ns-" + self.get_random_string(10)
|
|
109
111
|
self.deploy_namespace(bad_namespace, [])
|
|
110
112
|
self.deploy_fake_kraken(
|
|
111
|
-
bad_namespace,
|
|
113
|
+
namespace=bad_namespace,
|
|
114
|
+
random_label=None,
|
|
115
|
+
node_name="do_not_exist",
|
|
112
116
|
)
|
|
113
117
|
status = self.lib_k8s.monitor_component(
|
|
114
118
|
iteration=1, component_namespace=bad_namespace
|
|
@@ -67,6 +67,8 @@ class KrknKubernetesTestsList(BaseTest):
|
|
|
67
67
|
namespace = "test-lp" + self.get_random_string(10)
|
|
68
68
|
self.deploy_namespace(namespace, [])
|
|
69
69
|
self.deploy_fake_kraken(namespace=namespace)
|
|
70
|
+
|
|
71
|
+
# Test basic pod listing
|
|
70
72
|
pods = self.lib_k8s.list_pods(namespace=namespace)
|
|
71
73
|
self.assertTrue(len(pods) == 1)
|
|
72
74
|
self.assertIn("kraken-deployment", pods)
|
|
@@ -83,7 +85,38 @@ class KrknKubernetesTestsList(BaseTest):
|
|
|
83
85
|
namespace=namespace, field_selector="status.phase=Terminating"
|
|
84
86
|
)
|
|
85
87
|
self.assertTrue(len(pods) == 0)
|
|
88
|
+
|
|
89
|
+
# Test with exclude_label - should not exclude
|
|
90
|
+
# any pods (no matching labels)
|
|
91
|
+
pods = self.lib_k8s.list_pods(
|
|
92
|
+
namespace=namespace, exclude_label="skip=true"
|
|
93
|
+
)
|
|
94
|
+
self.assertTrue(len(pods) == 1)
|
|
95
|
+
self.assertIn("kraken-deployment", pods)
|
|
96
|
+
|
|
97
|
+
# Add a pod with the exclude label leveraging random_label will set
|
|
98
|
+
# random=skip
|
|
99
|
+
self.deploy_fake_kraken(
|
|
100
|
+
namespace=namespace, name="kraken-exclude", random_label="skip"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Test listing all pods without exclusion
|
|
104
|
+
pods = self.lib_k8s.list_pods(namespace=namespace)
|
|
105
|
+
self.assertTrue(len(pods) == 2)
|
|
106
|
+
self.assertIn("kraken-deployment", pods)
|
|
107
|
+
self.assertIn("kraken-exclude", pods)
|
|
108
|
+
|
|
109
|
+
# Test with exclude_label - should exclude the labeled pod
|
|
110
|
+
pods = self.lib_k8s.list_pods(
|
|
111
|
+
namespace=namespace, exclude_label="random=skip"
|
|
112
|
+
)
|
|
113
|
+
self.assertTrue(len(pods) == 1)
|
|
114
|
+
self.assertIn("kraken-deployment", pods)
|
|
115
|
+
self.assertNotIn("kraken-exclude", pods)
|
|
116
|
+
|
|
117
|
+
# Clean up
|
|
86
118
|
self.pod_delete_queue.put(["kraken-deployment", namespace])
|
|
119
|
+
self.pod_delete_queue.put(["kraken-exclude", namespace])
|
|
87
120
|
|
|
88
121
|
def test_list_ready_nodes(self):
|
|
89
122
|
try:
|
|
@@ -441,8 +441,12 @@ class TestKrknKubernetesPodsMonitor(BaseTest):
|
|
|
441
441
|
pods_status = snapshot.get_pods_status()
|
|
442
442
|
self.background_delete_ns(namespace)
|
|
443
443
|
self.assertEqual(len(pods_status.recovered), 1)
|
|
444
|
-
self.assertEqual(pods_status.recovered[0].pod_rescheduling_time,
|
|
444
|
+
self.assertEqual(pods_status.recovered[0].pod_rescheduling_time, 0)
|
|
445
445
|
self.assertGreater(pods_status.recovered[0].pod_readiness_time, 0)
|
|
446
|
+
self.assertEqual(
|
|
447
|
+
pods_status.recovered[0].total_recovery_time,
|
|
448
|
+
pods_status.recovered[0].pod_readiness_time,
|
|
449
|
+
)
|
|
446
450
|
|
|
447
451
|
def test_monitor_stopping_earlier(self):
|
|
448
452
|
|
|
@@ -426,7 +426,25 @@ class UtilFunctionTests(BaseTest):
|
|
|
426
426
|
os.environ["JOB_NAME"] = "1953335493844275200"
|
|
427
427
|
ci_job_url = utils.get_ci_job_url()
|
|
428
428
|
print("ci job url" + str(ci_job_url))
|
|
429
|
-
self.assertIn(
|
|
429
|
+
self.assertIn(
|
|
430
|
+
"prow.ci.openshift.org/view/gs/origin-ci-test", ci_job_url
|
|
431
|
+
)
|
|
432
|
+
os.environ["PULL_NUMBER"] = "68493"
|
|
433
|
+
os.environ["PROW_JOB_ID"] = os.environ["JOB_NAME"] = (
|
|
434
|
+
"1965813126276321280"
|
|
435
|
+
)
|
|
436
|
+
os.environ["JOB_TYPE"] = "presubmit"
|
|
437
|
+
|
|
438
|
+
os.environ["BUILD_ID"] = (
|
|
439
|
+
"rehearse-68493-periodic-ci-redhat-chaos-prow-scripts-main-4.20-nightly-krkn-hub-node-tests-aws-ipsec" # NOQA
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
ci_job_url = utils.get_ci_job_url()
|
|
443
|
+
print("ci job url" + str(ci_job_url))
|
|
444
|
+
self.assertIn(
|
|
445
|
+
"prow.ci.openshift.org/view/gs/test-platform-results", ci_job_url
|
|
446
|
+
)
|
|
447
|
+
|
|
430
448
|
os.environ["PROW_JOB_ID"] = ""
|
|
431
449
|
os.environ["BUILD_URL"] = (
|
|
432
450
|
"https://jenkins-csb-openshift-qe-mastern.dno.corp.redhat.com/job/scale-ci/job/e2e-benchmarking-multibranch-pipeline/job/kraken/" # NOQA
|
|
@@ -472,10 +472,25 @@ def get_ci_job_url():
|
|
|
472
472
|
prow_base_url = (
|
|
473
473
|
"https://prow.ci.openshift.org/view/gs/origin-ci-test/logs"
|
|
474
474
|
)
|
|
475
|
+
|
|
476
|
+
prow_pr_base_url = "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_release" # NOQA
|
|
475
477
|
task_id = os.getenv("BUILD_ID")
|
|
476
478
|
job_id = os.getenv("JOB_NAME")
|
|
477
|
-
|
|
478
|
-
|
|
479
|
+
pull_number = os.getenv("PULL_NUMBER")
|
|
480
|
+
job_type = os.getenv("JOB_TYPE")
|
|
481
|
+
if job_type == "presubmit" and "pull" in task_id:
|
|
482
|
+
# Indicates a ci test triggered in PR against source code
|
|
483
|
+
job_type = "pull"
|
|
484
|
+
if job_type == "presubmit" and "rehearse" in task_id:
|
|
485
|
+
# Indicates a rehearsel in PR against openshift/release repo
|
|
486
|
+
job_type = "pull"
|
|
487
|
+
# Handle cases where a periodic job iw triggered via pull request
|
|
488
|
+
if job_type == "periodic" and pull_number:
|
|
489
|
+
job_type = "pull"
|
|
490
|
+
if job_type == "pull":
|
|
491
|
+
build_url = f"{prow_pr_base_url}/{pull_number}/{task_id}/{job_id}"
|
|
492
|
+
else:
|
|
493
|
+
build_url = f"{prow_base_url}/{job_id}/{task_id}"
|
|
479
494
|
|
|
480
495
|
elif os.getenv("BUILD_URL", ""):
|
|
481
496
|
# Jenkins build url
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/k8s/templates/service_hijacking_config_map.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{krkn_lib-5.1.6 → krkn_lib-5.1.8}/src/krkn_lib/tests/test_krkn_kubernetes_pods_monitor_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|