Skip to content

Commit

Permalink
Create the node functionalities for IBMCloud BM, and create a new tes…
Browse files Browse the repository at this point in the history
…t file for testing the node restart functionalities

Signed-off-by: Itzhak Kave <[email protected]>
  • Loading branch information
Itzhak Kave committed Jan 25, 2024
1 parent 9d255ed commit 514ceea
Show file tree
Hide file tree
Showing 3 changed files with 504 additions and 1 deletion.
108 changes: 107 additions & 1 deletion ocs_ci/ocs/platform_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@
get_module_ip,
get_terraform_ignition_provider,
)
from ocs_ci.ocs.node import wait_for_nodes_status, get_nodes_in_statuses
from ocs_ci.ocs.node import (
wait_for_nodes_status,
get_nodes_in_statuses,
get_node_internal_ip,
)
from ocs_ci.utility.vsphere_nodes import VSPHERENode
from paramiko.ssh_exception import NoValidConnectionsError, AuthenticationException
from semantic_version import Version
Expand Down Expand Up @@ -92,6 +96,7 @@ def __init__(self):
"rosa": AWSNodes,
"vsphere_upi": VMWareUPINodes,
"fusion_aas": AWSNodes,
"hci_baremetal": IBMCloudBMNodes,
}

def get_nodes_platform(self):
Expand Down Expand Up @@ -3070,3 +3075,104 @@ def restart_nodes_by_stop_and_start_teardown(self):
node_names = [n.name for n in not_ready_nodes]
if node_names:
self.gcp.start_instances(node_names)


class IBMCloudBMNodes(NodesBase):
"""
IBM Cloud for Bare metal machines class
"""

def __init__(self):
super(IBMCloudBMNodes, self).__init__()
from ocs_ci.utility import ibmcloud_bm

self.ibmcloud_bm = ibmcloud_bm.IBMCloudBM()

def get_machines(self, nodes):
"""
Get the machines associated with the given nodes
Args:
nodes (list): The OCS objects of the nodes
Returns:
list: List of dictionaries. List of the machines associated with the given nodes
"""
node_ips = [get_node_internal_ip(n) for n in nodes]
return self.ibmcloud_bm.get_machines_by_ips(node_ips)

def stop_nodes(self, nodes):
"""
Stop nodes
Args:
nodes (list): The OCS objects of the nodes
"""
machines = self.get_machines(nodes)
self.ibmcloud_bm.stop_machines(machines)

def start_nodes(self, nodes):
"""
Start nodes
Args:
nodes (list): The OCS objects of the nodes
"""
machines = self.get_machines(nodes)
self.ibmcloud_bm.start_machines(machines)

def restart_nodes(self, nodes, force=False):
"""
Restart nodes
Args:
nodes (list): The OCS objects of the nodes
force (bool): If True, it will force restarting the nodes. False, otherwise.
Default value is False.
"""
machines = self.get_machines(nodes)
self.ibmcloud_bm.restart_machines(machines, force=force)

def restart_nodes_by_stop_and_start(self, nodes, wait=True, timeout=300):
"""
Restart the nodes by stop and start
Args:
nodes (list): The OCS objects of the nodes
wait (bool): If True, wait for the nodes to be ready. False, otherwise
timeout (int): The time to wait for the nodes to be ready
"""
machines = self.get_machines(nodes)
self.ibmcloud_bm.restart_machines_by_stop_and_start(machines)
if wait:
node_names = [n.name for n in nodes]
wait_for_nodes_status(node_names, timeout=timeout)

def restart_nodes_by_stop_and_start_teardown(self):
"""
Start the nodes in a NotReady state
"""
nodes_not_ready = get_nodes_in_statuses([constants.NODE_NOT_READY])
machines = self.get_machines(nodes_not_ready)
self.ibmcloud_bm.start_machines(machines)

def create_nodes(self, node_conf, node_type, num_nodes):
"""
Create nodes
"""
raise NotImplementedError("Create nodes functionality not implemented")

def terminate_nodes(self, nodes, wait=True):
"""
Terminate nodes
"""
raise NotImplementedError("terminate nodes functionality is not implemented")
153 changes: 153 additions & 0 deletions ocs_ci/utility/ibmcloud_bm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# -*- coding: utf8 -*-
"""
Module for interactions with IBM Cloud Cluster.
"""

import json
import logging
import time

from ocs_ci.framework import config
from ocs_ci.ocs.exceptions import CommandFailed
from ocs_ci.utility.utils import run_cmd


logger = logging.getLogger(name=__file__)
ibm_config = config.AUTH.get("ibmcloud", {})


def login():
"""
Login to IBM Cloud account
"""
api_key = ibm_config["api_key"]
login_cmd = f"ibmcloud login --apikey {api_key}"
account_id = ibm_config.get("account_id")
if account_id:
login_cmd += f" -c {account_id}"
api_endpoint = ibm_config.get("api_endpoint")
if api_endpoint:
login_cmd += f" -a {api_endpoint}"
region = config.ENV_DATA.get("region")
if region:
login_cmd += f" -r {region}"
logger.info("Logging to IBM cloud")
run_cmd(login_cmd, secrets=[api_key])
logger.info("Successfully logged in to IBM cloud")
config.RUN["ibmcloud_last_login"] = time.time()


def run_ibmcloud_bm_cmd(cmd, secrets=None, timeout=600, ignore_error=False, **kwargs):
"""
Wrapper function for `run_cmd` which if needed will perform IBM Cloud login
command before running the ibmcloud bare metal command. In the case run_cmd will fail
because the IBM cloud got disconnected, it will login and re-try.
Args:
cmd (str): command to run
secrets (list): A list of secrets to be masked with asterisks
This kwarg is popped in order to not interfere with
subprocess.run(``**kwargs``)
timeout (int): Timeout for the command, defaults to 600 seconds.
ignore_error (bool): True if ignore non zero return code and do not
raise the exception.
"""
last_login = config.RUN.get("ibmcloud_last_login", 0)
timeout_from_last_login = time.time() - last_login
basic_cmd = "ibmcloud sl hardware "
cmd = basic_cmd + cmd

# Login if the timeout from last login is greater than 9.5 minutes.
if not last_login or timeout_from_last_login > 570:
login()
try:
return run_cmd(cmd, secrets, timeout, ignore_error, **kwargs)
except CommandFailed as ex:
if "Please login" in str(ex):
login()
return run_cmd(cmd, secrets, timeout, ignore_error, **kwargs)


class IBMCloudBM(object):
"""
Wrapper for Ibm Cloud with Bare metal machines
"""

def get_all_machines(self):
"""
Get all the machines in the IBMCloud Bare metal machines
Returns:
list: List of dictionaries. List of all the machines in the IBMCloud Bare metal machines
"""
cmd = "list --output json"
machine_list = json.loads(run_ibmcloud_bm_cmd(cmd))
return machine_list

def get_machines_by_ips(self, machine_ips):
"""
Get the machines in the IBMCloud Bare metal machines that have the given machine IPs
Args:
machine_ips (list): The list of the machine IPs to search for.
Returns:
Get the machines in the IBMCloud Bare metal machines that have the given machine IPs
"""
machine_list = self.get_all_machines()
return [m for m in machine_list if m["primaryIpAddress"] in machine_ips]

def stop_machines(self, machines):
"""
Stop the IBMCloud Bare metal machines
Args:
machines (list): List of the IBMCLoud Bare metal machines objects to stop
"""
for m in machines:
logger.info(f"Powering off the machine with ip {m['primaryIpAddress']}")
cmd = f"power-off {m['id']} -f"
run_ibmcloud_bm_cmd(cmd)

def start_machines(self, machines):
"""
Start the IBMCloud Bare metal machines
Args:
machines (list): List of the IBMCLoud Bare metal machines objects to start
"""
for m in machines:
logger.info(f"Powering on the machine with ip {m['primaryIpAddress']}")
cmd = f"power-on {m['id']}"
run_ibmcloud_bm_cmd(cmd)

def restart_machines(self, machines, force=False):
"""
Reboot the IBMCloud Bare metal machines
Args:
machines (list): List of the IBMCLoud Bare metal machines objects to restart
force (bool): If False, will perform a soft reboot. Otherwise, if True, will perform a hard reboot
"""
reboot_type = "hard" if force else "soft"
for m in machines:
logger.info(f"Reboot the machine with the ip {m['primaryIpAddress']}")
cmd = f"reboot {m['id']} -f --{reboot_type}"
run_ibmcloud_bm_cmd(cmd)

def restart_machines_by_stop_and_start(self, machines):
"""
Restart the IBMCloud Bare metal machines by stop and start
Args:
machines (list): List of the IBMCLoud Bare metal machines objects to restart
"""
self.stop_machines(machines)
self.start_machines(machines)
Loading

0 comments on commit 514ceea

Please sign in to comment.