-
Notifications
You must be signed in to change notification settings - Fork 4
Continued changes for NNF fencing #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| #!@PYTHON@ | ||
|
|
||
| # Fence agent for Near Node Flash devices | ||
|
|
||
| import sys | ||
| import logging | ||
| import atexit | ||
| import http | ||
|
|
||
| sys.path.append("@FENCEAGENTSLIBDIR@") | ||
|
|
||
| from fencing import * | ||
| from fencing import fail, run_delay | ||
|
|
||
| try: | ||
| from kubernetes import client | ||
| except ImportError: | ||
| logging.error("Couldn't import kubernetes.client - not found or not accessible") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why handle this one specifically?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it has to be manually installed using pip |
||
|
|
||
| def get_power_status(api_client, options): | ||
| # Power status is the status of the Kubernetes Node resource if it is down, or | ||
| # the Fencing status of the NNF Node resource if the Kubernetes Node is up. | ||
|
|
||
| name = options.get("--nnf-node-name") | ||
| logging.debug("Reading node resource %s", name) | ||
|
|
||
| try: | ||
| node = client.CoreV1Api(api_client).read_node(name) | ||
|
|
||
| except client.exceptions.ApiException as e: | ||
| logging.debug("Exception when reading node: %s %s", e, type(e)) | ||
| if e.status == http.HTTPStatus.UNAUTHORIZED: | ||
| fail(EC_LOGIN_DENIED) | ||
| elif e.status == http.HTTPStatus.NOT_FOUND: | ||
| fail(EC_STATUS) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could use http.HTTPStatus for these codes |
||
|
|
||
| logging.error("Exception when reading node: %s %s", e, type(e)) | ||
|
|
||
| for condition in node.status.conditions: | ||
| if condition.type == "Ready" and condition.status == "True": | ||
| return _get_nnf_node_power_status(api_client, options) | ||
|
|
||
| logging.info("Node %s is not ready", name) | ||
| return "off" | ||
|
|
||
| def _get_nnf_node_power_status(api_client, options): | ||
|
|
||
| version = options.get("--api-version") | ||
| namespace = options.get("--nnf-node-name") | ||
| logging.debug("Reading NNF node resource %s/%s", version, namespace) | ||
|
|
||
| try: | ||
| node = client.CustomObjectsApi(api_client).get_namespaced_custom_object( | ||
| "nnf.cray.hpe.com", | ||
| version, | ||
| namespace, | ||
| "nnfnodes", | ||
| "nnf-nlc" | ||
| ) | ||
|
|
||
| except client.exceptions.ApiException as e: | ||
| logging.debug("Exception when reading NNF node: %s %s", e, type(e)) | ||
| if e.status == http.HTTPStatus.NOT_FOUND: | ||
| fail(EC_STATUS) | ||
|
|
||
| logging.error("Exception when reading NNF node: %s %s", e, type(e)) | ||
|
|
||
| fenced = node["status"].get("fenced", False) | ||
| logging.info("NNF Node fenced status: %s", fenced) | ||
|
|
||
| return "off" if fenced else "on" | ||
|
|
||
| def set_power_status(api_client, options): | ||
|
|
||
| if options.get("--action") == "on": | ||
| # NNF Fencing Agent is not permitted to enable the power status; that is done via | ||
| # adminsitrator actions. | ||
| return | ||
|
|
||
| api_object = client.CustomObjectsApi(api_client) | ||
|
|
||
| version = options.get("--api-version") | ||
| namespace = options.get("--nnf-node-name") | ||
|
|
||
| node = api_object.get_namespaced_custom_object( | ||
| "nnf.cray.hpe.com", | ||
| version, | ||
| namespace, | ||
| "nnfnodes", | ||
| "nnf-nlc" | ||
| ) | ||
|
|
||
| if not node["status"].get("fenced", False): | ||
| node["status"]["fenced"] = True | ||
| api_object.patch_namespaced_custom_object_status( | ||
| "nnf.cray.hpe.com", | ||
| version, | ||
| namespace, | ||
| "nnfnodes", | ||
| "nnf-nlc", | ||
| node | ||
| ) | ||
|
|
||
|
|
||
| def define_new_opts(): | ||
| all_opt["kubernetes-service-host"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "kubernetes-service-host", | ||
| "help" : "--kubernetes-service-host=[ADDRESS] The IP Address of the kubeapi server", | ||
| "shortdesc" : "Kubernetes API service IP address or hostname", | ||
| "required" : "0", | ||
| "order" : 1 | ||
| } | ||
|
|
||
| all_opt["kubernetes-service-port"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "kubernetes-service-port", | ||
| "help" : "--kubernetes-service-port=[PORT] The listen port of the kubeapi server", | ||
| "shortdesc" : "Kubernetes API service port", | ||
| "required" : "0", | ||
| "order" : 1 | ||
| } | ||
|
|
||
| all_opt["service-token-file"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "service-token-file", | ||
| "help" : "--service-token-file=[PATH] The path to the service token file", | ||
| "shortdesc" : "Path to the service token file", | ||
| "required" : "1", | ||
| "order" : 2 | ||
| } | ||
|
|
||
| all_opt["service-cert-file"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "service-cert-file", | ||
| "help" : "--service-cert-file=[PATH] The path to the service certificate file", | ||
| "shortdesc" : "Path to the service certificate file", | ||
| "required" : "1", | ||
| "order" : 2 | ||
| } | ||
|
|
||
| all_opt["nnf-node-name"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "nnf-node-name", | ||
| "help" : "--nnf-node-name=[PATH] The name of the NNF node", | ||
| "shortdesc" : "The name of the NNF node as listed in the system configuration", | ||
| "required" : "1", | ||
| "order" : 3 | ||
| } | ||
|
|
||
| all_opt["api-version"] = { | ||
| "getopt" : ":", | ||
| "longopt" : "api-version", | ||
| "help" : "--api-version=[VERSION] Version of the NNF Node API", | ||
| "shortdesc" : "The API Version of the NNF node resource", | ||
| "required" : "0", | ||
| "default" : "v1alpha1", | ||
| "order" : 4 | ||
| } | ||
|
|
||
| all_opt["localconfig"] = { | ||
| "getopt" : "", | ||
| "longopt" : "localconfig", | ||
| "help" : "--localconfig Use the local kubernetes config", | ||
| "required" : "0", | ||
| "order" : 4 | ||
| } | ||
|
|
||
| def main(): | ||
| atexit.register(atexit_handler) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's in fencing.py |
||
|
|
||
| device_opt = [ | ||
| "kubernetes-service-host", "kubernetes-service-port", | ||
| "service-token-file", "service-cert-file", | ||
| "nnf-node-name", | ||
| "api-version", | ||
| "localconfig", | ||
| "no_password" # signals to fencing.py to disable password requirement | ||
| ] | ||
|
|
||
| define_new_opts() | ||
|
|
||
| opt = process_input(device_opt) | ||
| options = check_input(device_opt, opt) | ||
|
|
||
| docs = {} | ||
| docs["shortdesc"] = "Fencing agent for Near Node Flash" | ||
| docs["longdesc"] = "fence_nnf is a fencing agent for Near Node Flash storage nodes." | ||
| docs["vendorurl"] = "https://nearnodeflash.github.io/" | ||
| show_docs(options, docs) | ||
|
|
||
| run_delay(options) | ||
|
|
||
| if "--localconfig" in options: | ||
| from kubernetes import config | ||
| configuration = config.load_kube_config() | ||
| else: | ||
| configuration = client.Configuration( | ||
| host="https://%s:%s" % ( | ||
| options.get("--kubernetes-service-host"), | ||
| options.get("--kubernetes-service-port") | ||
| ) | ||
| ) | ||
|
|
||
| with open(options.get("--service-token-file"), "r") as f: | ||
| token = f.read() | ||
|
|
||
| configuration.api_key = { "authorization" : token } | ||
| configuration.api_key_prefix = { "authorization" : "Bearer" } | ||
| configuration.ssl_ca_cert = options.get("--service-cert-file") | ||
|
|
||
| with client.ApiClient(configuration) as api_client: | ||
| result = fence_action(api_client, options, set_power_status, get_power_status) | ||
|
|
||
| sys.exit(result) | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| pcs stonith create stonith-rabbit-node-1 fence_nnf pcmk_host_list=rabbit-node-1 kubernetes-service-host=10.30.107.247 kubernetes-service-port=6443 service-token-file=/etc/nnf/service.token service-cert-file=/etc/nnf/service.cert nnf-node-name=rabbit-node-1 verbose=1 | ||
|
|
||
| pcs stonith create stonith-rabbit-compute-2 fence_redfish pcmk_host_list="rabbit-compute-2" ip=10.30.105.237 port=80 systems-uri=/redfish/v1/Systems/1 username=root password=REDACTED ssl_insecure=true verbose=1 | ||
|
|
||
| pcs stonith create stonith-rabbit-compute-3 fence_redfish pcmk_host_list="rabbit-compute-3" ip=10.30.105.253 port=80 systems-uri=/redfish/v1/Systems/1 username=root password=REDACTED ssl_insecure=true verbose=1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need a comment here. Does the user need to change this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, should have made it more obvious in the PR. This is just a place to stuff my changes so I have a copy if my machine were to crash. The final place will be in the ClusterLabs repo (or a fork), in which case configure/make will handle the string substitution.