145 lines
5.4 KiB
Diff
145 lines
5.4 KiB
Diff
|
diff --color -uNr a/agents/kubevirt/fence_kubevirt.py b/agents/kubevirt/fence_kubevirt.py
|
||
|
--- a/agents/kubevirt/fence_kubevirt.py 1970-01-01 01:00:00.000000000 +0100
|
||
|
+++ b/agents/kubevirt/fence_kubevirt.py 2021-10-25 13:25:59.904501348 +0200
|
||
|
@@ -0,0 +1,140 @@
|
||
|
+#!@PYTHON@ -tt
|
||
|
+
|
||
|
+import sys
|
||
|
+import logging
|
||
|
+sys.path.append("@FENCEAGENTSLIBDIR@")
|
||
|
+from fencing import *
|
||
|
+from fencing import fail, fail_usage, run_delay, EC_STATUS
|
||
|
+
|
||
|
+try:
|
||
|
+ sys.path.insert(0, '/usr/lib/fence-agents/bundled/kubevirt')
|
||
|
+ from kubernetes.client.exceptions import ApiException
|
||
|
+except ImportError:
|
||
|
+ logging.error("Couldn\'t import kubernetes.client.exceptions.ApiException - not found or not accessible")
|
||
|
+
|
||
|
+def get_nodes_list(conn, options):
|
||
|
+ logging.debug("Starting list/monitor operation")
|
||
|
+ result = {}
|
||
|
+ try:
|
||
|
+ apiversion = options.get("--apiversion")
|
||
|
+ namespace = options.get("--namespace")
|
||
|
+ include_uninitialized = True
|
||
|
+ vm_api = conn.resources.get(api_version=apiversion, kind='VirtualMachine')
|
||
|
+ vm_list = vm_api.get(namespace=namespace)
|
||
|
+ for vm in vm_list.items:
|
||
|
+ result[vm.metadata.name] = ("", None)
|
||
|
+ except Exception as e:
|
||
|
+ logging.error("Exception when calling VirtualMachine list: %s", e)
|
||
|
+ return result
|
||
|
+
|
||
|
+def get_power_status(conn, options):
|
||
|
+ logging.debug("Starting get status operation")
|
||
|
+ try:
|
||
|
+ apiversion = options.get("--apiversion")
|
||
|
+ namespace = options.get("--namespace")
|
||
|
+ name = options.get("--plug")
|
||
|
+ vmi_api = conn.resources.get(api_version=apiversion,
|
||
|
+ kind='VirtualMachineInstance')
|
||
|
+ vmi = vmi_api.get(name=name, namespace=namespace)
|
||
|
+ if vmi is not None:
|
||
|
+ phase = vmi.status.phase
|
||
|
+ if phase == "Running":
|
||
|
+ return "on"
|
||
|
+ return "off"
|
||
|
+ except ApiException as e:
|
||
|
+ if e.status == 404:
|
||
|
+ return "off"
|
||
|
+ logging.error("Failed to get power status, with API Exception: %s", e)
|
||
|
+ fail(EC_STATUS)
|
||
|
+ except Exception as e:
|
||
|
+ logging.error("Failed to get power status, with Exception: %s", e)
|
||
|
+ fail(EC_STATUS)
|
||
|
+
|
||
|
+def set_power_status(conn, options):
|
||
|
+ logging.debug("Starting set status operation")
|
||
|
+ try:
|
||
|
+ apiversion= options.get("--apiversion")
|
||
|
+ namespace = options.get("--namespace")
|
||
|
+ name = options.get("--plug")
|
||
|
+ action = 'start' if options["--action"] == "on" else 'stop'
|
||
|
+ virtctl_vm_action(conn, action, namespace, name, apiversion)
|
||
|
+ except Exception as e:
|
||
|
+ logging.error("Failed to set power status, with Exception: %s", e)
|
||
|
+ fail(EC_STATUS)
|
||
|
+
|
||
|
+def define_new_opts():
|
||
|
+ all_opt["namespace"] = {
|
||
|
+ "getopt" : ":",
|
||
|
+ "longopt" : "namespace",
|
||
|
+ "help" : "--namespace=[namespace] Namespace of the KubeVirt machine",
|
||
|
+ "shortdesc" : "Namespace of the KubeVirt machine.",
|
||
|
+ "required" : "1",
|
||
|
+ "order" : 2
|
||
|
+ }
|
||
|
+ all_opt["kubeconfig"] = {
|
||
|
+ "getopt" : ":",
|
||
|
+ "longopt" : "kubeconfig",
|
||
|
+ "help" : "--kubeconfig=[kubeconfig] Kubeconfig file path",
|
||
|
+ "shortdesc": "Kubeconfig file path",
|
||
|
+ "required": "0",
|
||
|
+ "order": 4
|
||
|
+ }
|
||
|
+ all_opt["apiversion"] = {
|
||
|
+ "getopt" : ":",
|
||
|
+ "longopt" : "apiversion",
|
||
|
+ "help" : "--apiversion=[apiversion] Version of the KubeVirt API",
|
||
|
+ "shortdesc" : "Version of the KubeVirt API.",
|
||
|
+ "required" : "0",
|
||
|
+ "default" : "kubevirt.io/v1",
|
||
|
+ "order" : 5
|
||
|
+ }
|
||
|
+
|
||
|
+def virtctl_vm_action(conn, action, namespace, name, apiversion):
|
||
|
+ path = '/apis/subresources.{api_version}/namespaces/{namespace}/virtualmachines/{name}/{action}'
|
||
|
+ path = path.format(api_version=apiversion, namespace=namespace, name=name, action=action)
|
||
|
+ return conn.request('put', path, header_params={'accept': '*/*'})
|
||
|
+
|
||
|
+def validate_options(required_options_list, options):
|
||
|
+ for required_option in required_options_list:
|
||
|
+ if required_option not in options:
|
||
|
+ fail_usage("Failed: %s option must be provided" % required_option)
|
||
|
+
|
||
|
+# Main agent method
|
||
|
+def main():
|
||
|
+ conn = None
|
||
|
+
|
||
|
+ device_opt = ["port", "namespace", "kubeconfig", "ssl_insecure", "no_password", "apiversion"]
|
||
|
+ define_new_opts()
|
||
|
+ options = check_input(device_opt, process_input(device_opt))
|
||
|
+
|
||
|
+ docs = {}
|
||
|
+ docs["shortdesc"] = "Fence agent for KubeVirt"
|
||
|
+ docs["longdesc"] = "fence_kubevirt is an I/O Fencing agent for KubeVirt."
|
||
|
+ docs["vendorurl"] = "https://kubevirt.io/"
|
||
|
+ show_docs(options, docs)
|
||
|
+
|
||
|
+ run_delay(options)
|
||
|
+
|
||
|
+ validate_options(['--namespace'], options)
|
||
|
+
|
||
|
+ # Disable insecure-certificate-warning message
|
||
|
+ if "--ssl-insecure" in options:
|
||
|
+ import urllib3
|
||
|
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||
|
+
|
||
|
+ try:
|
||
|
+ from kubernetes import config
|
||
|
+ from openshift.dynamic import DynamicClient
|
||
|
+ kubeconfig = options.get('--kubeconfig')
|
||
|
+ k8s_client = config.new_client_from_config(config_file=kubeconfig)
|
||
|
+ conn = DynamicClient(k8s_client)
|
||
|
+ except ImportError:
|
||
|
+ logging.error("Couldn\'t import kubernetes.config or "
|
||
|
+ "openshift.dynamic.DynamicClient - not found or not accessible")
|
||
|
+
|
||
|
+ # Operate the fencing device
|
||
|
+ result = fence_action(conn, options, set_power_status, get_power_status, get_nodes_list)
|
||
|
+ sys.exit(result)
|
||
|
+
|
||
|
+if __name__ == "__main__":
|
||
|
+ main()
|