diff --git a/config/config.yaml b/config/config.yaml index 7ec63789..bc124045 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -17,6 +17,7 @@ kraken: - scenarios/openshift/etcd.yml - scenarios/openshift/regex_openshift_pod_kill.yml - scenarios/openshift/vmware_node_scenarios.yml + - scenarios/openshift/ibmcloud_node_scenarios.yml - scenarios/openshift/network_chaos_ingress.yml - node_scenarios: # List of chaos node scenarios to load - scenarios/openshift/node_scenarios_example.yml diff --git a/docs/cloud_setup.md b/docs/cloud_setup.md index 17f4d7bc..3b9cf13e 100644 --- a/docs/cloud_setup.md +++ b/docs/cloud_setup.md @@ -1,11 +1,12 @@ Supported Cloud Providers: -* [AWS](#aws) -* [GCP](#gcp) -* [Openstack](#openstack) -* [Azure](#azure) -* [Alibaba](#alibaba) -* [VMware](#vmware) +- [AWS](#aws) +- [GCP](#gcp) +- [Openstack](#openstack) +- [Azure](#azure) +- [Alibaba](#alibaba) +- [VMware](#vmware) +- [IBMCloud](#ibmcloud) ## AWS @@ -65,4 +66,24 @@ Set the following environment variables 3. ```export VSPHERE_PASSWORD=``` -These are the credentials that you would normally use to access the vSphere client. \ No newline at end of file +These are the credentials that you would normally use to access the vSphere client. + + +## IBMCloud +If no api key is set up with proper VPC resource permissions, use the following to create: +* Access group +* Service id with the following access + * With policy **VPC Infrastructure Services** + * Resources = All + * Roles: + * Editor + * Administrator + * Operator + * Viewer +* API Key + +Set the following environment variables + +1. ```export IBMC_URL=https://.iaas.cloud.ibm.com/v1``` + +2. ```export IBMC_APIKEY=``` diff --git a/docs/node_scenarios.md b/docs/node_scenarios.md index 05f35a40..222de277 100644 --- a/docs/node_scenarios.md +++ b/docs/node_scenarios.md @@ -76,6 +76,42 @@ How to set up Alibaba cli to run node scenarios is defined [here](cloud_setup.md #### VMware How to set up VMware vSphere to run node scenarios is defined [here](cloud_setup.md#vmware) +This cloud type uses a different configuration style, see actions below and [example config file](../scenarios/openshift/vmware_node_scenarios.yml) + +*vmware-node-terminate, vmware-node-reboot, vmware-node-stop, vmware-node-start* + +#### IBMCloud +How to set up IBMCloud to run node scenarios is defined [here](cloud_setup.md#ibmcloud) + +This cloud type uses a different configuration style, see actions below and [example config file](../scenarios/openshift/ibmcloud_node_scenarios.yml) + +*ibmcloud-node-terminate, ibmcloud-node-reboot, ibmcloud-node-stop, ibmcloud-node-start +* + + +#### IBMCloud and Vmware example + + +``` +- id: ibmcloud-node-stop + config: + name: "" + label_selector: "node-role.kubernetes.io/worker" # When node_name is not specified, a node with matching label_selector is selected for node chaos scenario injection + runs: 1 # Number of times to inject each scenario under actions (will perform on same node each time) + instance_count: 1 # Number of nodes to perform action/select that match the label selector + timeout: 30 # Duration to wait for completion of node scenario injection + skip_openshift_checks: False # Set to True if you don't want to wait for the status of the nodes to change on OpenShift before passing the scenario +- id: ibmcloud-node-start + config: + name: "" #Same name as before + label_selector: "node-role.kubernetes.io/worker" # When node_name is not specified, a node with matching label_selector is selected for node chaos scenario injection + runs: 1 # Number of times to inject each scenario under actions (will perform on same node each time) + instance_count: 1 # Number of nodes to perform action/select that match the label selector + timeout: 30 # Duration to wait for completion of node scenario injection + skip_openshift_checks: False # Set to True if you don't want to wait for the status of the nodes to change on OpenShift before passing the scenario + ``` + + #### General diff --git a/kraken/plugins/__init__.py b/kraken/plugins/__init__.py index 9f9fb63d..7c4a9470 100644 --- a/kraken/plugins/__init__.py +++ b/kraken/plugins/__init__.py @@ -7,8 +7,8 @@ from arcaflow_plugin_sdk import schema, serialization, jsonschema from arcaflow_plugin_kill_pod import kill_pods, wait_for_pods - -import kraken.plugins.vmware.vmware_plugin as vmware_plugin +import kraken.plugins.node_scenarios.vmware_plugin as vmware_plugin +import kraken.plugins.node_scenarios.ibmcloud_plugin as ibmcloud_plugin from kraken.plugins.run_python_plugin import run_python_file from kraken.plugins.network.ingress_shaping import network_chaos @@ -182,7 +182,25 @@ def json_schema(self): ] ), PluginStep( - network_chaos, + ibmcloud_plugin.node_start, + [ + "error" + ] + ), + PluginStep( + ibmcloud_plugin.node_stop, + [ + "error" + ] + ), + PluginStep( + ibmcloud_plugin.node_reboot, + [ + "error" + ] + ), + PluginStep( + ibmcloud_plugin.node_terminate, [ "error" ] diff --git a/kraken/plugins/node_scenarios/ibmcloud_plugin.py b/kraken/plugins/node_scenarios/ibmcloud_plugin.py new file mode 100644 index 00000000..078bb10c --- /dev/null +++ b/kraken/plugins/node_scenarios/ibmcloud_plugin.py @@ -0,0 +1,563 @@ +#!/usr/bin/env python +import sys +import time +import typing +from os import environ +from dataclasses import dataclass, field +import random +from traceback import format_exc +import logging +from kraken.plugins.node_scenarios import kubernetes_functions as kube_helper +from arcaflow_plugin_sdk import validation, plugin +from kubernetes import client, watch +from ibm_vpc import VpcV1 +from ibm_cloud_sdk_core.authenticators import IAMAuthenticator +from ibm_cloud_sdk_core import ApiException +import requests +import sys + + +class IbmCloud: + def __init__(self): + """ + Initialize the ibm cloud client by using the the env variables: + 'IBMC_APIKEY' 'IBMC_URL' + """ + apiKey = environ.get("IBMC_APIKEY") + service_url = environ.get("IBMC_URL") + if not apiKey: + raise Exception( + "Environmental variable 'IBMC_APIKEY' is not set" + ) + if not service_url: + raise Exception( + "Environmental variable 'IBMC_URL' is not set" + ) + try: + authenticator = IAMAuthenticator(apiKey) + self.service = VpcV1(authenticator=authenticator) + + self.service.set_service_url(service_url) + except Exception as e: + logging.error("error authenticating" + str(e)) + sys.exit(1) + + def delete_instance(self, instance_id): + """ + Deletes the Instance whose name is given by 'instance_id' + """ + try: + self.service.delete_instance(instance_id) + logging.info("Deleted Instance -- '{}'".format(instance_id)) + except Exception as e: + logging.info( + "Instance '{}' could not be deleted. ".format( + instance_id + ) + ) + return False + + def reboot_instances(self, instance_id): + """ + Reboots the Instance whose name is given by 'instance_id'. Returns True if successful, or + returns False if the Instance is not powered on + """ + + try: + self.service.create_instance_action( + instance_id, + type='reboot', + ) + logging.info("Reset Instance -- '{}'".format(instance_id)) + return True + except Exception as e: + logging.info( + "Instance '{}' could not be rebooted".format( + instance_id + ) + ) + return False + + def stop_instances(self, instance_id): + """ + Stops the Instance whose name is given by 'instance_id'. Returns True if successful, or + returns False if the Instance is already stopped + """ + + try: + self.service.create_instance_action( + instance_id, + type='stop', + ) + logging.info("Stopped Instance -- '{}'".format(instance_id)) + return True + except Exception as e: + logging.info( + "Instance '{}' could not be stopped".format(instance_id) + ) + logging.info("error" + str(e)) + return False + + def start_instances(self, instance_id): + """ + Stops the Instance whose name is given by 'instance_id'. Returns True if successful, or + returns False if the Instance is already running + """ + + try: + self.service.create_instance_action( + instance_id, + type='start', + ) + logging.info("Started Instance -- '{}'".format(instance_id)) + return True + except Exception as e: + logging.info("Instance '{}' could not start running".format(instance_id)) + return False + + def list_instances(self): + """ + Returns a list of Instances present in the datacenter + """ + instance_names = [] + try: + instances_result = self.service.list_instances().get_result() + instances_list = instances_result['instances'] + for vpc in instances_list: + instance_names.append({"vpc_name": vpc['name'], "vpc_id": vpc['id']}) + starting_count = instances_result['total_count'] + while instances_result['total_count'] == instances_result['limit']: + instances_result = self.service.list_instances(start=starting_count).get_result() + instances_list = instances_result['instances'] + starting_count += instances_result['total_count'] + for vpc in instances_list: + instance_names.append({"vpc_name": vpc.name, "vpc_id": vpc.id}) + except Exception as e: + logging.error("Error listing out instances: " + str(e)) + sys.exit(1) + return instance_names + + def find_id_in_list(self, name, vpc_list): + for vpc in vpc_list: + if vpc['vpc_name'] == name: + return vpc['vpc_id'] + + def get_instance_status(self, instance_id): + """ + Returns the status of the Instance whose name is given by 'instance_id' + """ + + try: + instance = self.service.get_instance(instance_id).get_result() + state = instance['status'] + return state + except Exception as e: + logging.error( + "Failed to get node instance status %s. Encountered following " + "exception: %s." % (instance_id, e) + ) + return None + + def wait_until_deleted(self, instance_id, timeout): + """ + Waits until the instance is deleted or until the timeout. Returns True if + the instance is successfully deleted, else returns False + """ + + time_counter = 0 + vpc = self.get_instance_status(instance_id) + while vpc is not None: + vpc = self.get_instance_status(instance_id) + logging.info( + "Instance %s is still being deleted, sleeping for 5 seconds" % instance_id + ) + time.sleep(5) + time_counter += 5 + if time_counter >= timeout: + logging.info( + "Instance %s is still not deleted in allotted time" % instance_id + ) + return False + return True + + def wait_until_running(self, instance_id, timeout): + """ + Waits until the Instance switches to running state or until the timeout. + Returns True if the Instance switches to running, else returns False + """ + + time_counter = 0 + status = self.get_instance_status(instance_id) + while status != "running": + status = self.get_instance_status(instance_id) + logging.info( + "Instance %s is still not running, sleeping for 5 seconds" % instance_id + ) + time.sleep(5) + time_counter += 5 + if time_counter >= timeout: + logging.info("Instance %s is still not ready in allotted time" % instance_id) + return False + return True + + def wait_until_stopped(self, instance_id, timeout): + """ + Waits until the Instance switches to stopped state or until the timeout. + Returns True if the Instance switches to stopped, else returns False + """ + + time_counter = 0 + status = self.get_instance_status(instance_id) + while status != "stopped": + status = self.get_instance_status(instance_id) + logging.info( + "Instance %s is still not stopped, sleeping for 5 seconds" % instance_id + ) + time.sleep(5) + time_counter += 5 + if time_counter >= timeout: + logging.info("Instance %s is still not stopped in allotted time" % instance_id) + return False + return True + + def wait_until_rebooted(self, instance_id, timeout): + """ + Waits until the Instance switches to restarting state and then running state or until the timeout. + Returns True if the Instance switches back to running, else returns False + """ + + time_counter = 0 + status = self.get_instance_status(instance_id) + while status == "starting": + status = self.get_instance_status(instance_id) + logging.info( + "Instance %s is still restarting, sleeping for 5 seconds" % instance_id + ) + time.sleep(5) + time_counter += 5 + if time_counter >= timeout: + logging.info("Instance %s is still restarting after allotted time" % instance_id) + return False + self.wait_until_running(instance_id, timeout) + return True + + +@dataclass +class Node: + name: str + + +@dataclass +class NodeScenarioSuccessOutput: + + nodes: typing.Dict[int, Node] = field( + metadata={ + "name": "Nodes started/stopped/terminated/rebooted", + "description": """Map between timestamps and the pods started/stopped/terminated/rebooted. + The timestamp is provided in nanoseconds""", + } + ) + action: kube_helper.Actions = field( + metadata={ + "name": "The action performed on the node", + "description": """The action performed or attempted to be performed on the node. Possible values + are : Start, Stop, Terminate, Reboot""", + } + ) + + +@dataclass +class NodeScenarioErrorOutput: + + error: str + action: kube_helper.Actions = field( + metadata={ + "name": "The action performed on the node", + "description": """The action attempted to be performed on the node. Possible values are : Start + Stop, Terminate, Reboot""", + } + ) + + +@dataclass +class NodeScenarioConfig: + + name: typing.Annotated[ + typing.Optional[str], + validation.required_if_not("label_selector"), + validation.required_if("skip_openshift_checks"), + ] = field( + default=None, + metadata={ + "name": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set.", + }, + ) + + runs: typing.Annotated[typing.Optional[int], validation.min(1)] = field( + default=1, + metadata={ + "name": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)", + }, + ) + + label_selector: typing.Annotated[ + typing.Optional[str], + validation.min(1), + validation.required_if_not("name") + ] = field( + default=None, + metadata={ + "name": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\n" + "See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details.", + }, + ) + + timeout: typing.Annotated[typing.Optional[int], validation.min(1)] = field( + default=180, + metadata={ + "name": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds.", + }, + ) + + instance_count: typing.Annotated[typing.Optional[int], validation.min(1)] = field( + default=1, + metadata={ + "name": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector.", + }, + ) + + skip_openshift_checks: typing.Optional[bool] = field( + default=False, + metadata={ + "name": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes.", + }, + ) + + kubeconfig_path: typing.Optional[str] = field( + default=None, + metadata={ + "name": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\n" + "See https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for " + "details.", + }, + ) + + +@plugin.step( + id="ibmcloud-node-start", + name="Start the node", + description="Start the node(s) by starting the Ibmcloud Instance on which the node is configured", + outputs={"success": NodeScenarioSuccessOutput, "error": NodeScenarioErrorOutput}, +) +def node_start( + cfg: NodeScenarioConfig, +) -> typing.Tuple[ + str, typing.Union[NodeScenarioSuccessOutput, NodeScenarioErrorOutput] +]: + with kube_helper.setup_kubernetes(None) as cli: + ibmcloud = IbmCloud() + core_v1 = client.CoreV1Api(cli) + watch_resource = watch.Watch() + node_list = kube_helper.get_node_list(cfg, kube_helper.Actions.START, core_v1) + node_name_id_list = ibmcloud.list_instances() + nodes_started = {} + for name in node_list: + try: + for _ in range(cfg.runs): + logging.info("Starting node_start_scenario injection") + logging.info("Starting the node %s " % (name)) + instance_id = ibmcloud.find_id_in_list(name, node_name_id_list) + if instance_id: + vm_started = ibmcloud.start_instances(instance_id) + if vm_started: + ibmcloud.wait_until_running(instance_id, cfg.timeout) + if not cfg.skip_openshift_checks: + kube_helper.wait_for_ready_status( + name, cfg.timeout, watch_resource, core_v1 + ) + nodes_started[int(time.time_ns())] = Node(name=name) + logging.info("Node with instance ID: %s is in running state" % name) + logging.info("node_start_scenario has been successfully injected!") + else: + logging.error("Failed to find node that matched instances on ibm cloud in region") + return "error", NodeScenarioErrorOutput( + "No matching vpc with node name " + name, kube_helper.Actions.START + ) + except Exception as e: + logging.error("Failed to start node instance. Test Failed") + logging.error("node_start_scenario injection failed!") + return "error", NodeScenarioErrorOutput( + format_exc(), kube_helper.Actions.START + ) + + return "success", NodeScenarioSuccessOutput( + nodes_started, kube_helper.Actions.START + ) + + +@plugin.step( + id="ibmcloud-node-stop", + name="Stop the node", + description="Stop the node(s) by starting the Ibmcloud Instance on which the node is configured", + outputs={"success": NodeScenarioSuccessOutput, "error": NodeScenarioErrorOutput}, +) +def node_stop( + cfg: NodeScenarioConfig, +) -> typing.Tuple[ + str, typing.Union[NodeScenarioSuccessOutput, NodeScenarioErrorOutput] +]: + with kube_helper.setup_kubernetes(None) as cli: + ibmcloud = IbmCloud() + core_v1 = client.CoreV1Api(cli) + watch_resource = watch.Watch() + logging.info('set up done') + node_list = kube_helper.get_node_list(cfg, kube_helper.Actions.STOP, core_v1) + logging.info("set node list" + str(node_list)) + node_name_id_list = ibmcloud.list_instances() + logging.info('node names' + str(node_name_id_list)) + nodes_stopped = {} + for name in node_list: + try: + for _ in range(cfg.runs): + logging.info("Starting node_stop_scenario injection") + logging.info("Stopping the node %s " % (name)) + instance_id = ibmcloud.find_id_in_list(name, node_name_id_list) + if instance_id: + vm_stopped = ibmcloud.stop_instances(instance_id) + if vm_stopped: + ibmcloud.wait_until_stopped(instance_id, cfg.timeout) + if not cfg.skip_openshift_checks: + kube_helper.wait_for_ready_status( + name, cfg.timeout, watch_resource, core_v1 + ) + nodes_stopped[int(time.time_ns())] = Node(name=name) + logging.info("Node with instance ID: %s is in stopped state" % name) + logging.info("node_stop_scenario has been successfully injected!") + else: + logging.error("Failed to find node that matched instances on ibm cloud in region") + return "error", NodeScenarioErrorOutput( + "No matching vpc with node name " + name, kube_helper.Actions.STOP + ) + except Exception as e: + logging.error("Failed to stop node instance. Test Failed") + logging.error("node_stop_scenario injection failed!") + return "error", NodeScenarioErrorOutput( + format_exc(), kube_helper.Actions.STOP + ) + + return "success", NodeScenarioSuccessOutput( + nodes_stopped, kube_helper.Actions.STOP + ) + + +@plugin.step( + id="ibmcloud-node-reboot", + name="Reboot Ibmcloud Instance", + description="Reboot the node(s) by starting the Ibmcloud Instance on which the node is configured", + outputs={"success": NodeScenarioSuccessOutput, "error": NodeScenarioErrorOutput}, +) +def node_reboot( + cfg: NodeScenarioConfig, +) -> typing.Tuple[ + str, typing.Union[NodeScenarioSuccessOutput, NodeScenarioErrorOutput] +]: + with kube_helper.setup_kubernetes(None) as cli: + ibmcloud = IbmCloud() + core_v1 = client.CoreV1Api(cli) + watch_resource = watch.Watch() + node_list = kube_helper.get_node_list(cfg, kube_helper.Actions.REBOOT, core_v1) + node_name_id_list = ibmcloud.list_instances() + nodes_rebooted = {} + for name in node_list: + try: + for _ in range(cfg.runs): + logging.info("Starting node_reboot_scenario injection") + logging.info("Rebooting the node %s " % (name)) + instance_id = ibmcloud.find_id_in_list(name, node_name_id_list) + if instance_id: + ibmcloud.reboot_instances(instance_id) + ibmcloud.wait_until_rebooted(instance_id, cfg.timeout) + if not cfg.skip_openshift_checks: + kube_helper.wait_for_unknown_status( + name, cfg.timeout, watch_resource, core_v1 + ) + kube_helper.wait_for_ready_status( + name, cfg.timeout, watch_resource, core_v1 + ) + nodes_rebooted[int(time.time_ns())] = Node(name=name) + logging.info( + "Node with instance ID: %s has rebooted successfully" % name + ) + logging.info("node_reboot_scenario has been successfully injected!") + else: + logging.error("Failed to find node that matched instances on ibm cloud in region") + return "error", NodeScenarioErrorOutput( + "No matching vpc with node name " + name, kube_helper.Actions.REBOOT + ) + except Exception as e: + logging.error("Failed to reboot node instance. Test Failed") + logging.error("node_reboot_scenario injection failed!") + return "error", NodeScenarioErrorOutput( + format_exc(), kube_helper.Actions.REBOOT + ) + + return "success", NodeScenarioSuccessOutput( + nodes_rebooted, kube_helper.Actions.REBOOT + ) + + +@plugin.step( + id="ibmcloud-node-terminate", + name="Reboot Ibmcloud Instance", + description="Wait for node to be deleted", + outputs={"success": NodeScenarioSuccessOutput, "error": NodeScenarioErrorOutput}, +) +def node_terminate( + cfg: NodeScenarioConfig, +) -> typing.Tuple[ + str, typing.Union[NodeScenarioSuccessOutput, NodeScenarioErrorOutput] +]: + with kube_helper.setup_kubernetes(None) as cli: + ibmcloud = IbmCloud() + core_v1 = client.CoreV1Api(cli) + node_list = kube_helper.get_node_list( + cfg, kube_helper.Actions.TERMINATE, core_v1 + ) + node_name_id_list = ibmcloud.list_instances() + nodes_terminated = {} + for name in node_list: + try: + for _ in range(cfg.runs): + logging.info( + "Starting node_termination_scenario injection by first stopping the node" + ) + instance_id = ibmcloud.find_id_in_list(name, node_name_id_list) + logging.info("Deleting the node with instance ID: %s " % (name)) + if instance_id: + ibmcloud.delete_instance(instance_id) + ibmcloud.wait_until_released(name, cfg.timeout) + nodes_terminated[int(time.time_ns())] = Node(name=name) + logging.info("Node with instance ID: %s has been released" % name) + logging.info("node_terminate_scenario has been successfully injected!") + else: + logging.error("Failed to find instances that matched the node specifications on ibm cloud in the set region") + return "error", NodeScenarioErrorOutput( + "No matching vpc with node name " + name, kube_helper.Actions.TERMINATE + ) + except Exception as e: + logging.error("Failed to terminate node instance. Test Failed") + logging.error("node_terminate_scenario injection failed!") + return "error", NodeScenarioErrorOutput( + format_exc(), kube_helper.Actions.TERMINATE + ) + + return "success", NodeScenarioSuccessOutput( + nodes_terminated, kube_helper.Actions.TERMINATE + ) diff --git a/kraken/plugins/vmware/kubernetes_functions.py b/kraken/plugins/node_scenarios/kubernetes_functions.py similarity index 100% rename from kraken/plugins/vmware/kubernetes_functions.py rename to kraken/plugins/node_scenarios/kubernetes_functions.py diff --git a/kraken/plugins/vmware/vmware_plugin.py b/kraken/plugins/node_scenarios/vmware_plugin.py similarity index 98% rename from kraken/plugins/vmware/vmware_plugin.py rename to kraken/plugins/node_scenarios/vmware_plugin.py index 9996ee3c..7d17f8a5 100644 --- a/kraken/plugins/vmware/vmware_plugin.py +++ b/kraken/plugins/node_scenarios/vmware_plugin.py @@ -7,7 +7,6 @@ from dataclasses import dataclass, field from os import environ from traceback import format_exc - import requests from arcaflow_plugin_sdk import plugin, validation from com.vmware.vapi.std.errors_client import (AlreadyInDesiredState, @@ -17,7 +16,7 @@ from kubernetes import client, watch from vmware.vapi.vsphere.client import create_vsphere_client -from kraken.plugins.vmware import kubernetes_functions as kube_helper +from kraken.plugins.node_scenarios import kubernetes_functions as kube_helper class vSphere: @@ -534,7 +533,7 @@ class NodeScenarioConfig: @plugin.step( - id="node_start_scenario", + id="vmware-node-start", name="Start the node", description="Start the node(s) by starting the VMware VM " "on which the node is configured", @@ -593,7 +592,7 @@ def node_start( @plugin.step( - id="node_stop_scenario", + id="vmware-node-stop", name="Stop the node", description="Stop the node(s) by starting the VMware VM " "on which the node is configured", @@ -652,7 +651,7 @@ def node_stop( @plugin.step( - id="node_reboot_scenario", + id="vmware-node-reboot", name="Reboot VMware VM", description="Reboot the node(s) by starting the VMware VM " "on which the node is configured", @@ -713,13 +712,10 @@ def node_reboot( @plugin.step( - id="node_terminate_scenario", + id="vmware-node-terminate", name="Reboot VMware VM", - description="Wait for the specified number of pods to be present", - outputs={ - "success": NodeScenarioSuccessOutput, - "error": NodeScenarioErrorOutput - }, + description="Wait for the node to be terminated", + outputs={"success": NodeScenarioSuccessOutput, "error": NodeScenarioErrorOutput}, ) def node_terminate( cfg: NodeScenarioConfig, diff --git a/requirements.txt b/requirements.txt index 1591f6f9..b3a2d0ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,6 +32,7 @@ wheel service_identity git+https://github.com/vmware/vsphere-automation-sdk-python.git@v8.0.0.0 git+https://github.com/redhat-chaos/arcaflow-plugin-kill-pod.git - arcaflow >= 0.3.0 prometheus_api_client +ibm_cloud_sdk_core +ibm_vpc diff --git a/scenarios/openshift/ibmcloud_node_scenarios.yml b/scenarios/openshift/ibmcloud_node_scenarios.yml new file mode 100644 index 00000000..956ac869 --- /dev/null +++ b/scenarios/openshift/ibmcloud_node_scenarios.yml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../plugin.schema.json +- id: + config: + name: "" + label_selector: "node-role.kubernetes.io/worker" # When node_name is not specified, a node with matching label_selector is selected for node chaos scenario injection + runs: 1 # Number of times to inject each scenario under actions (will perform on same node each time) + instance_count: 1 # Number of nodes to perform action/select that match the label selector + timeout: 30 # Duration to wait for completion of node scenario injection + skip_openshift_checks: False # Set to True if you don't want to wait for the status of the nodes to change on OpenShift before passing the scenario \ No newline at end of file diff --git a/scenarios/openshift/vmware_node_scenarios.yml b/scenarios/openshift/vmware_node_scenarios.yml index 6d3c51c0..9acf5257 100644 --- a/scenarios/openshift/vmware_node_scenarios.yml +++ b/scenarios/openshift/vmware_node_scenarios.yml @@ -1,5 +1,5 @@ # yaml-language-server: $schema=../plugin.schema.json -- id: +- id: config: name: # Node on which scenario has to be injected; can set multiple names separated by comma label_selector: # When node_name is not specified, a node with matching label_selector is selected for node chaos scenario injection diff --git a/scenarios/plugin.schema.json b/scenarios/plugin.schema.json index 1eb1768d..b87b7090 100644 --- a/scenarios/plugin.schema.json +++ b/scenarios/plugin.schema.json @@ -15,6 +15,60 @@ "const": "kill-pods" }, "config": { + "$defs": { + "KillPodConfig": { + "type": "object", + "properties": { + "namespace_pattern": { + "type": "string", + "format": "regex", + "title": "Namespace pattern", + "description": "Regular expression for target pod namespaces." + }, + "name_pattern": { + "type": "string", + "format": "regex", + "title": "Name pattern", + "description": "Regular expression for target pods. Required if label_selector is not set." + }, + "kill": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of pods to kill", + "description": "How many pods should we attempt to kill?" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target pods. Required if name_pattern is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + }, + "timeout": { + "type": "integer", + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "backoff": { + "type": "integer", + "default": 1, + "title": "Backoff", + "description": "How many seconds to wait between checks for the target pod status." + } + }, + "required": [ + "namespace_pattern" + ], + "additionalProperties": false, + "dependentRequired": {} + } + }, "type": "object", "properties": { "namespace_pattern": { @@ -32,6 +86,7 @@ "kill": { "type": "integer", "minimum": 1, + "default": 1, "title": "Number of pods to kill", "description": "How many pods should we attempt to kill?" }, @@ -48,19 +103,22 @@ }, "timeout": { "type": "integer", + "default": 180, "title": "Timeout", "description": "Timeout to wait for the target pod(s) to be removed in seconds." }, "backoff": { "type": "integer", + "default": 1, "title": "Backoff", "description": "How many seconds to wait between checks for the target pod status." } }, - "additionalProperties": false, "required": [ "namespace_pattern" - ] + ], + "additionalProperties": false, + "dependentRequired": {} } }, "required": [ @@ -76,49 +134,96 @@ "const": "wait-for-pods" }, "config": { + "$defs": { + "WaitForPodsConfig": { + "type": "object", + "properties": { + "namespace_pattern": { + "type": "string", + "format": "regex" + }, + "name_pattern": { + "type": "string", + "format": "regex" + }, + "label_selector": { + "type": "string", + "minLength": 1 + }, + "count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Pod count", + "description": "Wait for at least this many pods to exist" + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "How many seconds to wait for?" + }, + "backoff": { + "type": "integer", + "default": 1, + "title": "Backoff", + "description": "How many seconds to wait between checks for the target pod status." + }, + "kubeconfig_path": { + "type": "string" + } + }, + "required": [ + "namespace_pattern" + ], + "additionalProperties": false, + "dependentRequired": {} + } + }, "type": "object", "properties": { "namespace_pattern": { "type": "string", - "format": "regex", - "title": "namespace_pattern" + "format": "regex" }, "name_pattern": { "type": "string", - "format": "regex", - "title": "name_pattern" + "format": "regex" }, "label_selector": { "type": "string", - "minLength": 1, - "title": "label_selector" + "minLength": 1 }, "count": { "type": "integer", "minimum": 1, + "default": 1, "title": "Pod count", "description": "Wait for at least this many pods to exist" }, "timeout": { "type": "integer", "minimum": 1, + "default": 180, "title": "Timeout", "description": "How many seconds to wait for?" }, "backoff": { "type": "integer", + "default": 1, "title": "Backoff", "description": "How many seconds to wait between checks for the target pod status." }, "kubeconfig_path": { - "type": "string", - "title": "kubeconfig_path" + "type": "string" } }, - "additionalProperties": false, "required": [ "namespace_pattern" - ] + ], + "additionalProperties": false, + "dependentRequired": {} } }, "required": [ @@ -134,17 +239,1824 @@ "const": "run_python" }, "config": { + "$defs": { + "RunPythonFileInput": { + "type": "object", + "properties": { + "filename": { + "type": "string" + } + }, + "required": [ + "filename" + ], + "additionalProperties": false, + "dependentRequired": {} + } + }, "type": "object", "properties": { "filename": { - "type": "string", - "title": "filename" + "type": "string" } }, - "additionalProperties": false, "required": [ "filename" - ] + ], + "additionalProperties": false, + "dependentRequired": {} + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "vmware-node-start" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "vmware-node-stop" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "vmware-node-reboot" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "vmware-node-terminate" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "verify_session": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": true, + "title": "Verify API Session", + "description": "Verifies the vSphere client session. It is enabled by default" + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "ibmcloud-node-start" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "ibmcloud-node-stop" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "ibmcloud-node-reboot" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "required": [ + "id", + "config" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "const": "ibmcloud-node-terminate" + }, + "config": { + "$defs": { + "NodeScenarioConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Name(s) for target nodes. Required if label_selector is not set." + }, + "runs": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Number of runs per node", + "description": "Number of times to inject each scenario under actions (will perform on same node each time)" + }, + "label_selector": { + "type": "string", + "minLength": 1, + "title": "Label selector", + "description": "Kubernetes label selector for the target nodes. Required if name is not set.\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for details." + }, + "timeout": { + "type": "integer", + "minimum": 1, + "default": 180, + "title": "Timeout", + "description": "Timeout to wait for the target pod(s) to be removed in seconds." + }, + "instance_count": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Instance Count", + "description": "Number of nodes to perform action/select that match the label selector." + }, + "skip_openshift_checks": { + "anyOf": [ + { + "title": "Boolean", + "type": "boolean" + }, + { + "title": "String", + "type": "string", + "enum": [ + "yes", + "y", + "true", + "on", + "enable", + "enabled", + "1", + "no", + "n", + "false", + "off", + "disable", + "disabled", + "0" + ] + }, + { + "title": "Integer", + "type": "integer", + "maximum": 1, + "minimum": 0 + } + ], + "default": false, + "title": "Skip Openshift Checks", + "description": "Skip checking the status of the openshift nodes." + }, + "kubeconfig_path": { + "type": "string", + "title": "Kubeconfig path", + "description": "Path to your Kubeconfig file. Defaults to ~/.kube/config.\nSee https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ for details." + } + }, + "required": [], + "additionalProperties": false, + "dependentRequired": { + "name": [ + "skip_openshift_checks" + ] + } } }, "required": [ diff --git a/tests/test_vmware_plugin.py b/tests/test_vmware_plugin.py index b00c8bbc..058beabe 100644 --- a/tests/test_vmware_plugin.py +++ b/tests/test_vmware_plugin.py @@ -2,8 +2,8 @@ import os import logging from arcaflow_plugin_sdk import plugin -from kraken.plugins.vmware.kubernetes_functions import Actions -from kraken.plugins.vmware import vmware_plugin +from kraken.plugins.node_scenarios.kubernetes_functions import Actions +from kraken.plugins.node_scenarios import vmware_plugin class NodeScenariosTest(unittest.TestCase):