forked from red-hat-storage/ocs-ci
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CNV automation: Classes and helper functions - Virtctl, VirtualMachin…
…e, VirtualMachineInstance and VM workloads (red-hat-storage#9248) Signed-off-by: Prasad Desala <[email protected]>
- Loading branch information
1 parent
c39f205
commit bd282d9
Showing
12 changed files
with
1,509 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
""" | ||
Helper functions specific for CNV | ||
""" | ||
import os | ||
import base64 | ||
import logging | ||
|
||
from ocs_ci.helpers.helpers import create_unique_resource_name, create_resource | ||
from ocs_ci.ocs import constants | ||
from ocs_ci.utility import templating | ||
from ocs_ci.ocs.cnv.virtual_machine import VirtualMachine | ||
from ocs_ci.helpers.helpers import ( | ||
wait_for_resource_state, | ||
create_ocs_object_from_kind_and_name, | ||
) | ||
from ocs_ci.framework import config | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def create_vm_using_standalone_pvc( | ||
namespace=constants.CNV_NAMESPACE, | ||
vm_name=None, | ||
pvc_size=None, | ||
sc_name=None, | ||
ssh=True, | ||
running=True, | ||
wait_for_vm_boot=True, | ||
vm_dict_path=None, | ||
): | ||
""" | ||
Create a Virtual Machine (VM) in the specified namespace using a standalone Persistent Volume Claim (PVC) | ||
Args: | ||
namespace (str): The namespace in which to create the VM. | ||
vm_name (str): Name for the VM. If not provided, a unique name will be generated. | ||
pvc_size (str): The size of the PVC to create | ||
sc_name (str): Storageclass name to use | ||
ssh (bool): If set to True, it adds a statically manged public SSH key during the VM creation at the first boot | ||
running (bool): Set to True for the VM to start upon it's creation, False otherwise | ||
wait_for_vm_boot (bool): If True and running is True, wait for the VM to finish booting and | ||
ensure SSH connectivity | ||
vm_dict_path (str): Path to the VM YAML file | ||
Returns: | ||
vm_obj: The VirtualMachine object | ||
Raises: | ||
CommandFailed: If an error occurs during the creation of the VM | ||
""" | ||
namespace = ( | ||
namespace if namespace else create_unique_resource_name("test-vm", "namespace") | ||
) | ||
source_data_obj = create_volume_import_source() | ||
pvc_data_obj = create_pvc_using_data_source( | ||
source_name=source_data_obj.name, | ||
pvc_size=pvc_size, | ||
sc_name=sc_name, | ||
namespace=namespace, | ||
) | ||
|
||
vm_dict_path = ( | ||
vm_dict_path if vm_dict_path else constants.CNV_VM_STANDALONE_PVC_VM_YAML | ||
) | ||
vm_data = templating.load_yaml(vm_dict_path) | ||
vm_name = vm_name if vm_name else create_unique_resource_name("test", "vm") | ||
vm_data["metadata"]["name"] = vm_name | ||
vm_data["metadata"]["namespace"] = namespace | ||
if not running: | ||
vm_data["spec"]["running"] = False | ||
vm_data["spec"]["template"]["spec"]["volumes"][0]["persistentVolumeClaim"][ | ||
"claimName" | ||
] = pvc_data_obj.name | ||
|
||
if ssh: | ||
ssh_secret = create_vm_secret(namespace=namespace) | ||
ssh_secret_dict = [ | ||
{ | ||
"sshPublicKey": { | ||
"propagationMethod": {"noCloud": {}}, | ||
"source": {"secret": {"secretName": f"{ssh_secret.name}"}}, | ||
} | ||
} | ||
] | ||
vm_data["spec"]["template"]["spec"]["accessCredentials"] = ssh_secret_dict | ||
|
||
vm_ocs_obj = create_resource(**vm_data) | ||
logger.info(f"Successfully created VM: {vm_ocs_obj.name}") | ||
|
||
wait_for_resource_state( | ||
resource=pvc_data_obj, state=constants.STATUS_BOUND, timeout=300 | ||
) | ||
vm_obj = VirtualMachine(vm_name=vm_ocs_obj.name, namespace=namespace) | ||
if running: | ||
vm_obj.wait_for_vm_status(status=constants.VM_RUNNING) | ||
if wait_for_vm_boot: | ||
vm_obj.wait_for_ssh_connectivity(timeout=1200) | ||
|
||
return vm_obj | ||
|
||
|
||
def get_ssh_pub_key_with_filename(path=None): | ||
""" | ||
Retrieve the content of the SSH public key and its file name | ||
Args: | ||
path (str): Path to the SSH public key file - Optional | ||
Returns: | ||
tuple: A tuple containing the content of the SSH public key and the file name | ||
Raises: | ||
FileNotFoundError: If the provided ssh pub key path does not exist | ||
""" | ||
logger.info( | ||
"Retrieving the content and file name of the SSH public key from the client machine" | ||
) | ||
ssh_dir = os.path.expanduser("~/.ssh/") | ||
if path: | ||
if os.path.exists(path): | ||
ssh_key_path = path | ||
logger.info(f"The provided ssh pub key path:{path} exists") | ||
else: | ||
raise FileNotFoundError( | ||
f"The provided ssh pub key path:{path} does not exist" | ||
) | ||
else: | ||
id_rsa_path = os.path.join(ssh_dir, "id_rsa.pub") | ||
config_ssh_key = config.DEPLOYMENT.get("ssh_key") | ||
config_ssh_key_path = os.path.expanduser(config_ssh_key) | ||
if os.path.exists(id_rsa_path): | ||
ssh_key_path = id_rsa_path | ||
logger.info("Default id_rsa.pub exists") | ||
elif config_ssh_key and os.path.exists(config_ssh_key_path): | ||
ssh_key_path = config_ssh_key_path | ||
logger.info(f"Using ssh key from ocs-ci default config: {config_ssh_key}") | ||
else: | ||
raise FileNotFoundError( | ||
"Neither id_rsa.pub nor ssh_key in ocs-ci default config is present" | ||
) | ||
|
||
with open(ssh_key_path, "r") as ssh_key: | ||
content = ssh_key.read().strip() | ||
key_name = os.path.basename(ssh_key_path) | ||
|
||
return content, key_name | ||
|
||
|
||
def convert_ssh_key_to_base64(ssh_key): | ||
""" | ||
Convert SSH key to base64 encoding | ||
Args: | ||
ssh_key (str): SSH key | ||
Returns: | ||
str: Base64 encoded SSH key | ||
""" | ||
logger.info("Converting SSH key to base64") | ||
base64_key = base64.b64encode(ssh_key.encode()).decode() | ||
return base64_key | ||
|
||
|
||
def create_vm_secret(path=None, namespace=constants.CNV_NAMESPACE): | ||
""" | ||
Create an SSH secret for the VM | ||
Args: | ||
path (str): Path to the SSH public key file - optional | ||
Returns: | ||
secret_obj: An OCS instance | ||
""" | ||
secret_data = templating.load_yaml(constants.CNV_VM_SECRET_YAML) | ||
secret_data["metadata"]["name"] = create_unique_resource_name("vm-test", "secret") | ||
secret_data["metadata"]["namespace"] = namespace | ||
ssh_pub_key, _ = get_ssh_pub_key_with_filename(path=path) | ||
base64_key = convert_ssh_key_to_base64(ssh_key=ssh_pub_key) | ||
secret_data["data"]["key"] = base64_key | ||
secret_obj = create_resource(**secret_data) | ||
logger.info(f"Successfully created an SSH secret for the VM - {secret_obj.name}") | ||
|
||
return secret_obj | ||
|
||
|
||
def create_volume_import_source(name=None, url=None): | ||
""" | ||
Create a VolumeImportSource object | ||
Args: | ||
name (str): Name for the VolumeImportSource. If not provided, a unique name will be generated | ||
url (str): URL for the registry source | ||
Returns: | ||
source_data_obj: The created VolumeImportSource object | ||
""" | ||
source_data = templating.load_yaml(constants.CNV_VM_STANDALONE_PVC_SOURCE_YAML) | ||
name = name if name else create_unique_resource_name("source", "volumeimportsource") | ||
source_data["metadata"]["name"] = name | ||
if url: | ||
source_data["spec"]["source"]["registry"]["url"] | ||
source_data_obj = create_resource(**source_data) | ||
logger.info(f"Successfully created VolumeImportSource - {source_data_obj.name}") | ||
|
||
return source_data_obj | ||
|
||
|
||
def create_pvc_using_data_source( | ||
source_name, pvc_size=None, sc_name=None, namespace=constants.CNV_NAMESPACE | ||
): | ||
""" | ||
Create a PVC using a specified data source | ||
Args: | ||
source_name (str): Name of the data source (VolumeImportSource) for the PVC | ||
pvc_size (str): Size of the PVC | ||
sc_name (str): StorageClass name for the PVC | ||
namespace (str): The namespace in which to create the PVC | ||
Returns: | ||
pvc_data_obj: PVC object | ||
""" | ||
pvc_data = templating.load_yaml(constants.CNV_VM_STANDALONE_PVC_PVC_YAML) | ||
pvc_name = create_unique_resource_name("test", "pvc") | ||
pvc_data["metadata"]["name"] = pvc_name | ||
pvc_data["metadata"]["namespace"] = namespace | ||
pvc_data["spec"]["dataSourceRef"]["name"] = source_name | ||
if pvc_size: | ||
pvc_data["spec"]["resource"]["requests"]["storage"] = pvc_size | ||
if sc_name: | ||
pvc_data["spec"]["storageClassName"] = sc_name | ||
pvc_data_obj = create_resource(**pvc_data) | ||
logger.info(f"Successfully created PVC - {pvc_data_obj.name} using data source") | ||
|
||
return pvc_data_obj | ||
|
||
|
||
def get_pvc_from_vm(vm_obj): | ||
""" | ||
Get the PVC name from VM obj | ||
Returns: | ||
ocs_ci.ocs.resources.ocs.OCS (obj): PVC in the form of ocs object | ||
""" | ||
vm_data = vm_obj.get() | ||
pvc_name = ( | ||
vm_data.get("spec") | ||
.get("template") | ||
.get("spec") | ||
.get("volumes")[0] | ||
.get("persistentVolumeClaim") | ||
.get("claimName") | ||
) | ||
return create_ocs_object_from_kind_and_name( | ||
kind=constants.PVC, resource_name=pvc_name, namespace=vm_obj.namespace | ||
) | ||
|
||
|
||
def get_secret_from_vm(vm_obj): | ||
""" | ||
Get the secret name from VM obj | ||
Returns: | ||
ocs_ci.ocs.resources.ocs.OCS (obj): Secret in the form of ocs object | ||
""" | ||
vm_data = vm_obj.get() | ||
secret_name = ( | ||
vm_data.get("spec") | ||
.get("template") | ||
.get("spec") | ||
.get("accessCredentials")[0] | ||
.get("sshPublicKey") | ||
.get("source") | ||
.get("secret") | ||
.get("secretName") | ||
) | ||
return create_ocs_object_from_kind_and_name( | ||
kind=constants.SECRET, resource_name=secret_name, namespace=vm_obj.namespace | ||
) | ||
|
||
|
||
def get_volumeimportsource(pvc_obj): | ||
""" | ||
Get the volumeimportsource name from PVC obj | ||
Returns: | ||
ocs_ci.ocs.resources.ocs.OCS (obj): volumeimportsource in the form of ocs object | ||
""" | ||
pvc_data = pvc_obj.get() | ||
volumeimportsource_name = pvc_data.get("spec").get("dataSource").get("name") | ||
return create_ocs_object_from_kind_and_name( | ||
kind=constants.VOLUME_IMPORT_SOURCE, | ||
resource_name=volumeimportsource_name, | ||
namespace=pvc_obj.namespace, | ||
) | ||
|
||
|
||
def get_ssh_private_key_path(): | ||
""" | ||
Get the full path of the derived private key file from the associated SSH public key file | ||
Returns: | ||
str: The full path of the derived private key file | ||
""" | ||
ssh_dir = os.path.expanduser("~/.ssh/") | ||
_, ssh_pub_key_name = get_ssh_pub_key_with_filename() | ||
|
||
# Derive private key path by replacing the extension (if present) | ||
private_key_name, _ = os.path.splitext(ssh_pub_key_name) | ||
private_key_path = os.path.join(ssh_dir, private_key_name) | ||
|
||
# Handling both with and without .pem file extension case | ||
pem_private_key_path = private_key_path + ".pem" | ||
if os.path.exists(pem_private_key_path): | ||
private_key_path = pem_private_key_path | ||
logger.info( | ||
f"The private key used for authenticating to the server: {private_key_path}" | ||
) | ||
|
||
return private_key_path |
Oops, something went wrong.