openshift-python-wrapper 4.11.61__py3-none-any.whl

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 (132) hide show
  1. ocp_resources/__init__.py +0 -0
  2. ocp_resources/api_service.py +9 -0
  3. ocp_resources/backup.py +52 -0
  4. ocp_resources/benchmark.py +92 -0
  5. ocp_resources/catalog_source.py +63 -0
  6. ocp_resources/catalog_source_config.py +86 -0
  7. ocp_resources/cdi.py +13 -0
  8. ocp_resources/cdi_config.py +52 -0
  9. ocp_resources/chaos_engine.py +30 -0
  10. ocp_resources/chaos_result.py +11 -0
  11. ocp_resources/cluster_operator.py +5 -0
  12. ocp_resources/cluster_role.py +70 -0
  13. ocp_resources/cluster_role_binding.py +36 -0
  14. ocp_resources/cluster_service_version.py +8 -0
  15. ocp_resources/cluster_version.py +5 -0
  16. ocp_resources/configmap.py +37 -0
  17. ocp_resources/console_cli_download.py +9 -0
  18. ocp_resources/console_plugin.py +9 -0
  19. ocp_resources/console_quick_start.py +9 -0
  20. ocp_resources/constants.py +26 -0
  21. ocp_resources/csidriver.py +9 -0
  22. ocp_resources/custom_resource_definition.py +5 -0
  23. ocp_resources/daemonset.py +61 -0
  24. ocp_resources/data_import_cron.py +100 -0
  25. ocp_resources/data_source.py +59 -0
  26. ocp_resources/datavolume.py +275 -0
  27. ocp_resources/deployment.py +65 -0
  28. ocp_resources/destination_rule.py +11 -0
  29. ocp_resources/event.py +114 -0
  30. ocp_resources/forklift_controller.py +10 -0
  31. ocp_resources/gateway.py +11 -0
  32. ocp_resources/hook.py +52 -0
  33. ocp_resources/host.py +64 -0
  34. ocp_resources/hostpath_provisioner.py +50 -0
  35. ocp_resources/hyperconverged.py +43 -0
  36. ocp_resources/image.py +9 -0
  37. ocp_resources/image_content_source_policy.py +9 -0
  38. ocp_resources/image_stream.py +48 -0
  39. ocp_resources/imagestreamtag.py +9 -0
  40. ocp_resources/infrastructure.py +18 -0
  41. ocp_resources/installplan.py +8 -0
  42. ocp_resources/job.py +65 -0
  43. ocp_resources/kube_descheduler.py +65 -0
  44. ocp_resources/kubevirt.py +5 -0
  45. ocp_resources/kubevirt_common_templates_bundle.py +5 -0
  46. ocp_resources/kubevirt_metrics_aggregation.py +5 -0
  47. ocp_resources/kubevirt_node_labeller_bundle.py +5 -0
  48. ocp_resources/kubevirt_template_validaotr.py +5 -0
  49. ocp_resources/logger.py +76 -0
  50. ocp_resources/machine.py +50 -0
  51. ocp_resources/machine_config.py +5 -0
  52. ocp_resources/machine_config_pool.py +9 -0
  53. ocp_resources/machine_health_check.py +76 -0
  54. ocp_resources/machine_set.py +189 -0
  55. ocp_resources/migration.py +63 -0
  56. ocp_resources/migration_policy.py +66 -0
  57. ocp_resources/mtv.py +171 -0
  58. ocp_resources/mutating_webhook_config.py +9 -0
  59. ocp_resources/namespace.py +40 -0
  60. ocp_resources/network.py +5 -0
  61. ocp_resources/network_addons_config.py +9 -0
  62. ocp_resources/network_attachment_definition.py +24 -0
  63. ocp_resources/network_map.py +64 -0
  64. ocp_resources/network_policy.py +9 -0
  65. ocp_resources/nmstate.py +9 -0
  66. ocp_resources/node.py +41 -0
  67. ocp_resources/node_health_check.py +10 -0
  68. ocp_resources/node_maintenance.py +40 -0
  69. ocp_resources/node_network_configuration_enactment.py +20 -0
  70. ocp_resources/node_network_configuration_policy.py +472 -0
  71. ocp_resources/node_network_state.py +121 -0
  72. ocp_resources/oauth.py +9 -0
  73. ocp_resources/operator_condition.py +5 -0
  74. ocp_resources/operator_group.py +37 -0
  75. ocp_resources/operator_hub.py +5 -0
  76. ocp_resources/operator_source.py +50 -0
  77. ocp_resources/package_manifest.py +5 -0
  78. ocp_resources/peer_authentication.py +18 -0
  79. ocp_resources/persistent_volume.py +27 -0
  80. ocp_resources/persistent_volume_claim.py +103 -0
  81. ocp_resources/plan.py +143 -0
  82. ocp_resources/pod.py +175 -0
  83. ocp_resources/pod_disruption_budget.py +53 -0
  84. ocp_resources/priority_class.py +48 -0
  85. ocp_resources/project.py +60 -0
  86. ocp_resources/prometheus.py +9 -0
  87. ocp_resources/prometheus_rule.py +9 -0
  88. ocp_resources/provider.py +59 -0
  89. ocp_resources/replicaset.py +11 -0
  90. ocp_resources/resource.py +1383 -0
  91. ocp_resources/restore.py +52 -0
  92. ocp_resources/role.py +11 -0
  93. ocp_resources/role_binding.py +67 -0
  94. ocp_resources/route.py +76 -0
  95. ocp_resources/secret.py +75 -0
  96. ocp_resources/security_context_constraints.py +9 -0
  97. ocp_resources/service.py +16 -0
  98. ocp_resources/service_account.py +9 -0
  99. ocp_resources/service_mesh_control_plane.py +11 -0
  100. ocp_resources/service_mesh_member_roll.py +14 -0
  101. ocp_resources/service_monitor.py +9 -0
  102. ocp_resources/sriov_network.py +54 -0
  103. ocp_resources/sriov_network_node_policy.py +67 -0
  104. ocp_resources/sriov_network_node_state.py +44 -0
  105. ocp_resources/ssp.py +9 -0
  106. ocp_resources/storage_class.py +47 -0
  107. ocp_resources/storage_map.py +67 -0
  108. ocp_resources/storage_profile.py +7 -0
  109. ocp_resources/subscription.py +66 -0
  110. ocp_resources/tekton_tasks.py +9 -0
  111. ocp_resources/template.py +74 -0
  112. ocp_resources/upload_token_request.py +39 -0
  113. ocp_resources/utils.py +278 -0
  114. ocp_resources/validating_webhook_config.py +9 -0
  115. ocp_resources/virtual_machine.py +177 -0
  116. ocp_resources/virtual_machine_Instance_replica_set.py +9 -0
  117. ocp_resources/virtual_machine_cluster_flavor.py +5 -0
  118. ocp_resources/virtual_machine_flavor.py +5 -0
  119. ocp_resources/virtual_machine_import.py +307 -0
  120. ocp_resources/virtual_machine_instance.py +322 -0
  121. ocp_resources/virtual_machine_instance_migration.py +36 -0
  122. ocp_resources/virtual_machine_instance_preset.py +9 -0
  123. ocp_resources/virtual_machine_restore.py +99 -0
  124. ocp_resources/virtual_machine_snapshot.py +96 -0
  125. ocp_resources/virtual_service.py +11 -0
  126. ocp_resources/vm_import_config.py +9 -0
  127. ocp_resources/volume_snapshot.py +9 -0
  128. ocp_resources/volume_snapshot_class.py +9 -0
  129. openshift_python_wrapper-4.11.61.dist-info/LICENSE +201 -0
  130. openshift_python_wrapper-4.11.61.dist-info/METADATA +188 -0
  131. openshift_python_wrapper-4.11.61.dist-info/RECORD +132 -0
  132. openshift_python_wrapper-4.11.61.dist-info/WHEEL +4 -0
@@ -0,0 +1,307 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ from ocp_resources.constants import PROTOCOL_ERROR_EXCEPTION_DICT, TIMEOUT_4MINUTES
5
+ from ocp_resources.resource import NamespacedResource
6
+ from ocp_resources.utils import TimeoutExpiredError, TimeoutSampler
7
+ from ocp_resources.virtual_machine import VirtualMachine
8
+
9
+
10
+ def _map_mappings(mappings):
11
+ mappings_list = []
12
+ for mapping in mappings:
13
+ mapping_dict = {"target": {"name": mapping.target_name}}
14
+ if mapping.target_namespace:
15
+ mapping_dict["target"]["namespace"] = mapping.target_namespace
16
+ if mapping.target_type:
17
+ mapping_dict["type"] = mapping.target_type
18
+ if mapping.source_id:
19
+ mapping_dict.setdefault("source", {})["id"] = mapping.source_id
20
+ if mapping.source_name:
21
+ mapping_dict.setdefault("source", {})["name"] = mapping.source_name
22
+ if mapping.target_access_modes:
23
+ mapping_dict["accessMode"] = mapping.target_access_modes
24
+ if mapping.target_volume_mode:
25
+ mapping_dict["volumeMode"] = mapping.target_volume_mode
26
+ mappings_list.append(mapping_dict)
27
+ return mappings_list
28
+
29
+
30
+ class VirtualMachineImport(NamespacedResource):
31
+ """
32
+ Virtual Machine Import object, inherited from NamespacedResource.
33
+ """
34
+
35
+ api_group = NamespacedResource.ApiGroup.V2V_KUBEVIRT_IO
36
+
37
+ class Condition(NamespacedResource.Condition):
38
+ SUCCEEDED = "Succeeded"
39
+ VALID = "Valid"
40
+ MAPPING_RULES_VERIFIED = "MappingRulesVerified"
41
+ PROCESSING = "Processing"
42
+
43
+ class ValidConditionReason:
44
+ """
45
+ Valid condition reason object
46
+ """
47
+
48
+ VALIDATION_COMPLETED = "ValidationCompleted"
49
+ SECRET_NOT_FOUND = "SecretNotFound" # pragma: allowlist secret
50
+ RESOURCE_MAPPING_NOT_FOUND = "ResourceMappingNotFound"
51
+ UNINITIALIZED_PROVIDER = "UninitializedProvider"
52
+ SOURCE_VM_NOT_FOUND = "SourceVMNotFound"
53
+ INCOMPLETE_MAPPING_RULES = "IncompleteMappingRules"
54
+
55
+ class MappingRulesConditionReason:
56
+ """
57
+ Mapping rules verified condition reason object
58
+ """
59
+
60
+ MAPPING_COMPLETED = "MappingRulesVerificationCompleted"
61
+ MAPPING_FAILED = "MappingRulesVerificationFailed"
62
+ MAPPING_REPORTED_WARNINGS = "MappingRulesVerificationReportedWarnings"
63
+
64
+ class ProcessingConditionReason:
65
+ """
66
+ Processing condition reason object
67
+ """
68
+
69
+ CREATING_TARGET_VM = "CreatingTargetVM"
70
+ COPYING_DISKS = "CopyingDisks"
71
+ COMPLETED = "ProcessingCompleted"
72
+ FAILED = "ProcessingFailed"
73
+
74
+ class SucceededConditionReason:
75
+ """
76
+ Succeeced cond reason object
77
+ """
78
+
79
+ VALIDATION_FAILED = "ValidationFailed"
80
+ VM_CREATION_FAILED = "VMCreationFailed"
81
+ DATAVOLUME_CREATION_FAILED = "DataVolumeCreationFailed"
82
+ VIRTUAL_MACHINE_READY = "VirtualMachineReady"
83
+ VIRTUAL_MACHINE_RUNNING = "VirtualMachineRunning"
84
+ VMTEMPLATE_MATCHING_FAILED = "VMTemplateMatchingFailed"
85
+
86
+ def __init__(
87
+ self,
88
+ name=None,
89
+ namespace=None,
90
+ provider_credentials_secret_name=None,
91
+ provider_type=None,
92
+ provider_credentials_secret_namespace=None,
93
+ client=None,
94
+ teardown=True,
95
+ vm_id=None,
96
+ vm_name=None,
97
+ cluster_id=None,
98
+ cluster_name=None,
99
+ target_vm_name=None,
100
+ start_vm=False,
101
+ provider_mappings=None,
102
+ resource_mapping_name=None,
103
+ resource_mapping_namespace=None,
104
+ warm=False,
105
+ finalize_date=None,
106
+ privileged_client=None,
107
+ yaml_file=None,
108
+ delete_timeout=TIMEOUT_4MINUTES,
109
+ **kwargs,
110
+ ):
111
+ super().__init__(
112
+ name=name,
113
+ namespace=namespace,
114
+ client=client,
115
+ teardown=teardown,
116
+ privileged_client=privileged_client,
117
+ yaml_file=yaml_file,
118
+ delete_timeout=delete_timeout,
119
+ **kwargs,
120
+ )
121
+ self.vm_id = vm_id
122
+ self.vm_name = vm_name
123
+ self.cluster_id = cluster_id
124
+ self.cluster_name = cluster_name
125
+ self.target_vm_name = target_vm_name
126
+ self.start_vm = start_vm
127
+ self.provider_credentials_secret_name = provider_credentials_secret_name
128
+ self.provider_credentials_secret_namespace = (
129
+ provider_credentials_secret_namespace
130
+ )
131
+ self.provider_mappings = provider_mappings
132
+ self.resource_mapping_name = resource_mapping_name
133
+ self.resource_mapping_namespace = resource_mapping_namespace
134
+ self.provider_type = provider_type
135
+ self.warm = warm
136
+ self.finalize_date = finalize_date
137
+
138
+ @property
139
+ def vm(self):
140
+ return VirtualMachine(
141
+ name=self.target_vm_name,
142
+ namespace=self.namespace,
143
+ client=self.client,
144
+ privileged_client=self.privileged_client or self.client,
145
+ )
146
+
147
+ def to_dict(self):
148
+ super().to_dict()
149
+ if not self.yaml_file:
150
+ spec = self.res.setdefault("spec", {})
151
+
152
+ secret = spec.setdefault("providerCredentialsSecret", {})
153
+ secret["name"] = self.provider_credentials_secret_name
154
+
155
+ if self.provider_credentials_secret_namespace:
156
+ secret["namespace"] = self.provider_credentials_secret_namespace
157
+
158
+ if self.resource_mapping_name:
159
+ spec.setdefault("resourceMapping", {})[
160
+ "name"
161
+ ] = self.resource_mapping_name
162
+ if self.resource_mapping_namespace:
163
+ spec.setdefault("resourceMapping", {})[
164
+ "namespace"
165
+ ] = self.resource_mapping_namespace
166
+
167
+ if self.target_vm_name:
168
+ spec["targetVmName"] = self.target_vm_name
169
+
170
+ if self.start_vm is not None:
171
+ spec["startVm"] = self.start_vm
172
+
173
+ if self.warm:
174
+ spec["warm"] = self.warm
175
+ if self.finalize_date:
176
+ spec["finalizeDate"] = self.finalize_date.strftime(
177
+ format="%Y-%m-%dT%H:%M:%SZ"
178
+ )
179
+
180
+ provider_source = spec.setdefault("source", {}).setdefault(
181
+ self.provider_type, {}
182
+ )
183
+ vm = provider_source.setdefault("vm", {})
184
+ if self.vm_id:
185
+ vm["id"] = self.vm_id
186
+ if self.vm_name:
187
+ vm["name"] = self.vm_name
188
+
189
+ if self.cluster_id:
190
+ vm.setdefault("cluster", {})["id"] = self.cluster_id
191
+ if self.cluster_name:
192
+ vm.setdefault("cluster", {})["name"] = self.cluster_name
193
+
194
+ if self.provider_mappings:
195
+ if self.provider_mappings.disk_mappings:
196
+ mappings = _map_mappings(
197
+ mappings=self.provider_mappings.disk_mappings
198
+ )
199
+ provider_source.setdefault("mappings", {}).setdefault(
200
+ "diskMappings", mappings
201
+ )
202
+
203
+ if self.provider_mappings.network_mappings:
204
+ mappings = _map_mappings(
205
+ mappings=self.provider_mappings.network_mappings
206
+ )
207
+ provider_source.setdefault("mappings", {}).setdefault(
208
+ "networkMappings", mappings
209
+ )
210
+
211
+ if self.provider_mappings.storage_mappings:
212
+ mappings = _map_mappings(
213
+ mappings=self.provider_mappings.storage_mappings
214
+ )
215
+ provider_source.setdefault("mappings", {}).setdefault(
216
+ "storageMappings", mappings
217
+ )
218
+
219
+ def wait(
220
+ self,
221
+ timeout=600,
222
+ cond_reason=SucceededConditionReason.VIRTUAL_MACHINE_READY,
223
+ cond_status=Condition.Status.TRUE,
224
+ cond_type=Condition.SUCCEEDED,
225
+ ):
226
+ self.logger.info(
227
+ f"Wait for {self.kind} {self.name} {cond_reason} condition to be {cond_status}"
228
+ )
229
+ samples = TimeoutSampler(
230
+ wait_timeout=timeout,
231
+ sleep=1,
232
+ exceptions_dict=PROTOCOL_ERROR_EXCEPTION_DICT,
233
+ func=self.api.get,
234
+ field_selector=f"metadata.name=={self.name}",
235
+ namespace=self.namespace,
236
+ )
237
+ last_condition = None
238
+ try:
239
+ for sample in samples:
240
+ if sample.items:
241
+ sample_status = sample.items[0].status
242
+ if sample_status:
243
+ current_conditions = sample_status.conditions
244
+ for cond in current_conditions:
245
+ last_condition = cond
246
+ if (
247
+ cond.type == cond_type
248
+ and cond.status == cond_status
249
+ and cond.reason == cond_reason
250
+ ):
251
+ msg = (
252
+ f"Status of {self.kind} {self.name} {cond.type} is "
253
+ f"{cond.status} ({cond.reason}: {cond.message})"
254
+ )
255
+ self.logger.info(msg)
256
+ return
257
+ except TimeoutExpiredError:
258
+ raise TimeoutExpiredError(
259
+ f"Last condition of {self.kind} {self.name} {last_condition.type} was "
260
+ f"{last_condition.status} ({last_condition.reason}: {last_condition.message})"
261
+ )
262
+
263
+
264
+ class ResourceMapping(NamespacedResource):
265
+ """
266
+ ResourceMapping object.
267
+ """
268
+
269
+ api_group = NamespacedResource.ApiGroup.V2V_KUBEVIRT_IO
270
+
271
+ def __init__(
272
+ self,
273
+ name=None,
274
+ namespace=None,
275
+ mapping=None,
276
+ client=None,
277
+ teardown=True,
278
+ yaml_file=None,
279
+ **kwargs,
280
+ ):
281
+ super().__init__(
282
+ name=name,
283
+ namespace=namespace,
284
+ client=client,
285
+ teardown=teardown,
286
+ yaml_file=yaml_file,
287
+ **kwargs,
288
+ )
289
+ self.mapping = mapping
290
+
291
+ def to_dict(self):
292
+ super().to_dict()
293
+ if not self.yaml_file:
294
+ for provider, mapping in self.mapping.items():
295
+ res_provider_section = self.res.setdefault("spec", {}).setdefault(
296
+ provider, {}
297
+ )
298
+ if mapping.network_mappings is not None:
299
+ res_provider_section.setdefault(
300
+ "networkMappings",
301
+ _map_mappings(mappings=mapping.network_mappings),
302
+ )
303
+ if mapping.storage_mappings is not None:
304
+ res_provider_section.setdefault(
305
+ "storageMappings",
306
+ _map_mappings(mappings=mapping.storage_mappings),
307
+ )
@@ -0,0 +1,322 @@
1
+ import shlex
2
+
3
+ import xmltodict
4
+ from openshift.dynamic.exceptions import ResourceNotFoundError
5
+
6
+ from ocp_resources.constants import PROTOCOL_ERROR_EXCEPTION_DICT, TIMEOUT_4MINUTES
7
+ from ocp_resources.node import Node
8
+ from ocp_resources.pod import Pod
9
+ from ocp_resources.resource import NamespacedResource
10
+ from ocp_resources.utils import TimeoutExpiredError, TimeoutSampler
11
+
12
+
13
+ class VirtualMachineInstance(NamespacedResource):
14
+ """
15
+ Virtual Machine Instance object, inherited from Resource.
16
+ """
17
+
18
+ api_group = NamespacedResource.ApiGroup.KUBEVIRT_IO
19
+
20
+ class Status(NamespacedResource.Status):
21
+ SCHEDULING = "Scheduling"
22
+
23
+ def __init__(
24
+ self,
25
+ name=None,
26
+ namespace=None,
27
+ client=None,
28
+ privileged_client=None,
29
+ yaml_file=None,
30
+ delete_timeout=TIMEOUT_4MINUTES,
31
+ **kwargs,
32
+ ):
33
+ super().__init__(
34
+ name=name,
35
+ namespace=namespace,
36
+ client=client,
37
+ privileged_client=privileged_client,
38
+ yaml_file=yaml_file,
39
+ delete_timeout=delete_timeout,
40
+ **kwargs,
41
+ )
42
+
43
+ @property
44
+ def _subresource_api_url(self):
45
+ return (
46
+ f"{self.client.configuration.host}/"
47
+ f"apis/subresources.kubevirt.io/{self.api.api_version}/"
48
+ f"namespaces/{self.namespace}/virtualmachineinstances/{self.name}"
49
+ )
50
+
51
+ def api_request(self, method, action, **params):
52
+ return super().api_request(
53
+ method=method, action=action, url=self._subresource_api_url, **params
54
+ )
55
+
56
+ def pause(self, timeout=TIMEOUT_4MINUTES, wait=False):
57
+ self.api_request(method="PUT", action="pause")
58
+ if wait:
59
+ return self.wait_for_pause_status(pause=True, timeout=timeout)
60
+
61
+ def unpause(self, timeout=TIMEOUT_4MINUTES, wait=False):
62
+ self.api_request(method="PUT", action="unpause")
63
+ if wait:
64
+ return self.wait_for_pause_status(pause=False, timeout=timeout)
65
+
66
+ @property
67
+ def interfaces(self):
68
+ return self.instance.status.interfaces
69
+
70
+ @property
71
+ def virt_launcher_pod(self):
72
+ pods = list(
73
+ Pod.get(
74
+ dyn_client=self.privileged_client or self.client,
75
+ namespace=self.namespace,
76
+ label_selector=f"kubevirt.io=virt-launcher,kubevirt.io/created-by={self.instance.metadata.uid}",
77
+ )
78
+ )
79
+ migration_state = self.instance.status.migrationState
80
+ if migration_state:
81
+ # After VM migration there are two pods, one in Completed status and one in Running status.
82
+ # We need to return the Pod that is not in Completed status.
83
+ for pod in pods:
84
+ if migration_state.targetPod == pod.name:
85
+ return pod
86
+ else:
87
+ return pods[0]
88
+
89
+ raise ResourceNotFoundError
90
+
91
+ @property
92
+ def virt_handler_pod(self):
93
+ pods = list(
94
+ Pod.get(
95
+ dyn_client=self.privileged_client or self.client,
96
+ label_selector="kubevirt.io=virt-handler",
97
+ )
98
+ )
99
+ for pod in pods:
100
+ if pod.instance["spec"]["nodeName"] == self.instance.status.nodeName:
101
+ return pod
102
+
103
+ raise ResourceNotFoundError
104
+
105
+ def wait_until_running(self, timeout=TIMEOUT_4MINUTES, logs=True, stop_status=None):
106
+ """
107
+ Wait until VMI is running
108
+
109
+ Args:
110
+ timeout (int): Time to wait for VMI.
111
+ logs (bool): True to extract logs from the VMI pod and from the VMI.
112
+ stop_status (str): Status which should stop the wait and failed.
113
+
114
+ Raises:
115
+ TimeoutExpiredError: If VMI failed to run.
116
+ """
117
+ try:
118
+ self.wait_for_status(
119
+ status=self.Status.RUNNING, timeout=timeout, stop_status=stop_status
120
+ )
121
+ except TimeoutExpiredError:
122
+ if not logs:
123
+ raise
124
+
125
+ virt_pod = self.virt_launcher_pod
126
+ if virt_pod:
127
+ self.logger.debug(f"{virt_pod.name} *****LOGS*****")
128
+ self.logger.debug(virt_pod.log(container="compute"))
129
+
130
+ raise
131
+
132
+ def wait_for_pause_status(self, pause, timeout=TIMEOUT_4MINUTES):
133
+ """
134
+ Wait for Virtual Machine Instance to be paused / unpaused.
135
+ Paused status is checked in libvirt and in the VMI conditions.
136
+
137
+ Args:
138
+ pause (bool): True for paused, False for unpause
139
+ timeout (int): Time to wait for the resource.
140
+
141
+ Raises:
142
+ TimeoutExpiredError: If resource not exists.
143
+ """
144
+ self.logger.info(
145
+ f"Wait until {self.kind} {self.name} is "
146
+ f"{'Paused' if pause else 'Unpuased'}"
147
+ )
148
+ self.wait_for_domstate_pause_status(pause=pause, timeout=timeout)
149
+ self.wait_for_vmi_condition_pause_status(pause=pause, timeout=timeout)
150
+
151
+ def wait_for_domstate_pause_status(self, pause, timeout=TIMEOUT_4MINUTES):
152
+ pause_status = "paused" if pause else "running"
153
+ samples = TimeoutSampler(
154
+ wait_timeout=timeout,
155
+ sleep=1,
156
+ exceptions_dict=PROTOCOL_ERROR_EXCEPTION_DICT,
157
+ func=self.get_domstate,
158
+ )
159
+ for sample in samples:
160
+ if pause_status in sample:
161
+ return
162
+
163
+ def wait_for_vmi_condition_pause_status(self, pause, timeout=TIMEOUT_4MINUTES):
164
+ samples = TimeoutSampler(
165
+ wait_timeout=timeout,
166
+ sleep=1,
167
+ exceptions_dict=PROTOCOL_ERROR_EXCEPTION_DICT,
168
+ func=self.get_vmi_active_condition,
169
+ )
170
+ for sample in samples:
171
+ # VM in state change
172
+ # We have commanded a [un]pause condition via the API but the CR has not been updated yet to match.
173
+ # 'reason' may not exist yet
174
+ # or
175
+ # 'reason' may still exist after unpause if the CR has not been updated before we perform this check
176
+ if (pause and not sample.get("reason")) or (
177
+ sample.get("reason") == "PausedByUser" and not pause
178
+ ):
179
+ continue
180
+ # Paused VM
181
+ if pause and sample["reason"] == "PausedByUser":
182
+ return
183
+ # Unpaused VM
184
+ if not (pause and sample.get("reason")):
185
+ return
186
+
187
+ @property
188
+ def node(self):
189
+ """
190
+ Get the node name where the VM is running
191
+
192
+ Returns:
193
+ Node: Node
194
+ """
195
+ return Node(
196
+ client=self.privileged_client or self.client,
197
+ name=self.instance.status.nodeName,
198
+ )
199
+
200
+ def virsh_cmd(self, action):
201
+ return shlex.split(
202
+ f"virsh {self.virt_launcher_pod_hypervisor_connection_uri} {action} {self.namespace}_{self.name}"
203
+ )
204
+
205
+ def get_xml(self):
206
+ """
207
+ Get virtual machine instance XML
208
+
209
+ Returns:
210
+ xml_output(string): VMI XML in the multi-line string
211
+ """
212
+ return self.execute_virsh_command(command="dumpxml")
213
+
214
+ @property
215
+ def virt_launcher_pod_user_uid(self):
216
+ """
217
+ Get Virt Launcher Pod User UID value
218
+
219
+ Returns:
220
+ Int: Virt Launcher Pod UID value
221
+ """
222
+ return self.virt_launcher_pod.instance.spec.securityContext.runAsUser
223
+
224
+ @property
225
+ def is_virt_launcher_pod_root(self):
226
+ """
227
+ Check if Virt Launcher Pod is Root
228
+
229
+ Returns:
230
+ Bool: True if Virt Launcher Pod is Root.
231
+ """
232
+ return not bool(self.virt_launcher_pod_user_uid)
233
+
234
+ @property
235
+ def virt_launcher_pod_hypervisor_connection_uri(self):
236
+ """
237
+ Get Virt Launcher Pod Hypervisor Connection URI
238
+
239
+ Required to connect to Hypervisor for
240
+ Non-Root Virt-Launcher Pod.
241
+
242
+ Returns:
243
+ String: Hypervisor Connection URI
244
+ """
245
+ return (
246
+ ""
247
+ if self.is_virt_launcher_pod_root
248
+ else "-c qemu+unix:///session?socket=/var/run/libvirt/libvirt-sock"
249
+ )
250
+
251
+ def get_domstate(self):
252
+ """
253
+ Get virtual machine instance Status.
254
+
255
+ Current workaround, as VM/VMI shows no status/phase == Paused yet.
256
+ Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1805178
257
+
258
+ Returns:
259
+ String: VMI Status as string
260
+ """
261
+ return self.execute_virsh_command(command="domstate")
262
+
263
+ def get_dommemstat(self):
264
+ """
265
+ Get virtual machine domain memory stats
266
+ link: https://libvirt.org/manpages/virsh.html#dommemstat
267
+
268
+ Returns:
269
+ String: VMI domain memory stats as string
270
+ """
271
+ return self.execute_virsh_command(command="dommemstat")
272
+
273
+ def get_vmi_active_condition(self):
274
+ """A VMI may have multiple conditions; the active one it the one with
275
+ 'lastTransitionTime'"""
276
+ return {
277
+ k: v
278
+ for condition in self.instance.status.conditions
279
+ for k, v in condition.items()
280
+ if condition["lastTransitionTime"]
281
+ }
282
+
283
+ @property
284
+ def xml_dict(self):
285
+ """Get virtual machine instance XML as dict"""
286
+
287
+ return xmltodict.parse(xml_input=self.get_xml(), process_namespaces=True)
288
+
289
+ @property
290
+ def guest_os_info(self):
291
+ return self.api_request(method="GET", action="guestosinfo")
292
+
293
+ @property
294
+ def guest_fs_info(self):
295
+ return self.api_request(method="GET", action="filesystemlist")
296
+
297
+ @property
298
+ def guest_user_info(self):
299
+ return self.api_request(method="GET", action="userlist")
300
+
301
+ @property
302
+ def os_version(self):
303
+ vmi_os_version = self.instance.status.guestOSInfo.get("version", {})
304
+ if not vmi_os_version:
305
+ self.logger.warning(
306
+ "Guest agent is not installed on the VM; OS version is not available."
307
+ )
308
+ return vmi_os_version
309
+
310
+ def interface_ip(self, interface):
311
+ iface_ip = [
312
+ iface["ipAddress"]
313
+ for iface in self.interfaces
314
+ if iface["interfaceName"] == interface
315
+ ]
316
+ return iface_ip[0] if iface_ip else None
317
+
318
+ def execute_virsh_command(self, command):
319
+ return self.virt_launcher_pod.execute(
320
+ command=self.virsh_cmd(action=command),
321
+ container="compute",
322
+ )
@@ -0,0 +1,36 @@
1
+ from ocp_resources.constants import TIMEOUT_4MINUTES
2
+ from ocp_resources.resource import NamespacedResource
3
+
4
+
5
+ class VirtualMachineInstanceMigration(NamespacedResource):
6
+ api_group = NamespacedResource.ApiGroup.KUBEVIRT_IO
7
+
8
+ def __init__(
9
+ self,
10
+ name=None,
11
+ namespace=None,
12
+ vmi=None,
13
+ client=None,
14
+ teardown=True,
15
+ yaml_file=None,
16
+ delete_timeout=TIMEOUT_4MINUTES,
17
+ **kwargs,
18
+ ):
19
+ super().__init__(
20
+ name=name,
21
+ namespace=namespace,
22
+ client=client,
23
+ teardown=teardown,
24
+ yaml_file=yaml_file,
25
+ delete_timeout=delete_timeout,
26
+ **kwargs,
27
+ )
28
+ self._vmi = vmi
29
+
30
+ def to_dict(self):
31
+ # When creating VirtualMachineInstanceMigration vmi is mandatory but when calling get()
32
+ # we cannot pass vmi.
33
+ super().to_dict()
34
+ if not self.yaml_file:
35
+ assert self._vmi, "vmi is mandatory for create"
36
+ self.res["spec"] = {"vmiName": self._vmi.name}
@@ -0,0 +1,9 @@
1
+ from ocp_resources.resource import NamespacedResource
2
+
3
+
4
+ class VirtualMachineInstancePreset(NamespacedResource):
5
+ """
6
+ VirtualMachineInstancePreset object.
7
+ """
8
+
9
+ api_group = NamespacedResource.ApiGroup.KUBEVIRT_IO