|
| 1 | +# pylint:disable=too-many-arguments |
| 2 | +# pylint:disable=too-many-locals |
| 3 | +from email.header import Header |
| 4 | +from typing import List, Optional, Union |
| 5 | + |
| 6 | +from apis.email_api.emailer import Emailer |
| 7 | +from apis.email_api.structs.email_params import EmailParams |
| 8 | +from apis.email_api.structs.email_template_details import EmailTemplateDetails |
| 9 | +from apis.email_api.structs.smtp_account import SMTPAccount |
| 10 | +from apis.openstack_api.enums.cloud_domains import CloudDomains |
| 11 | +from tabulate import tabulate |
| 12 | +from workflows.send_decom_flavor_email import ( |
| 13 | + find_servers_with_decom_flavors, |
| 14 | + find_user_info, |
| 15 | +) |
| 16 | + |
| 17 | + |
| 18 | +def validate_input_arguments( |
| 19 | + flavor_name_list: List[str], |
| 20 | + from_projects: List[str] = None, |
| 21 | + all_projects: bool = False, |
| 22 | +): |
| 23 | + """ |
| 24 | + Validate input arguments for sending power testing emails. |
| 25 | +
|
| 26 | + :param flavor_name_list: List of OpenStack flavor names that are under power testing review. |
| 27 | + :param planned_date_ranges: List of planned power testing dates associated with the flavors. |
| 28 | + :param from_projects: (Optional) List of project names or IDs to restrict the search scope. |
| 29 | + :param all_projects: If True, searches across all OpenStack projects. |
| 30 | +
|
| 31 | + :raises RuntimeError: If required arguments are missing or if both `from_projects` and `all_projects` are set. |
| 32 | + """ |
| 33 | + if not flavor_name_list: |
| 34 | + raise RuntimeError("please provide a list of flavor names to decommission") |
| 35 | + |
| 36 | + if from_projects and all_projects: |
| 37 | + raise RuntimeError( |
| 38 | + "given both project list and all_projects flag - please choose only one" |
| 39 | + ) |
| 40 | + |
| 41 | + if not from_projects and not all_projects: |
| 42 | + raise RuntimeError( |
| 43 | + "please provide either a list of project identifiers or the 'all_projects' flag" |
| 44 | + ) |
| 45 | + |
| 46 | + |
| 47 | +def build_email_params( |
| 48 | + user_name: str, |
| 49 | + affected_flavors_table: str, |
| 50 | + affected_servers_table: str, |
| 51 | + **email_kwargs, |
| 52 | +): |
| 53 | + """ |
| 54 | + Construct EmailParams for notifying a user about flavors under power testing. |
| 55 | +
|
| 56 | + :param user_name: Name of the OpenStack user receiving the email. |
| 57 | + :param affected_flavors_table: A rendered table (plain or HTML) listing affected flavors. |
| 58 | + :param affected_servers_table: A rendered table listing the user's VMs using affected flavors. |
| 59 | + :param email_kwargs: Additional keyword arguments for the EmailParams class. |
| 60 | +
|
| 61 | + :return: EmailParams object containing templated email content and metadata. |
| 62 | + """ |
| 63 | + body = EmailTemplateDetails( |
| 64 | + template_name="power_testing", |
| 65 | + template_params={ |
| 66 | + "user_name": user_name, |
| 67 | + "affected_flavors_table": affected_flavors_table, |
| 68 | + "affected_servers_table": affected_servers_table, |
| 69 | + }, |
| 70 | + ) |
| 71 | + |
| 72 | + footer = EmailTemplateDetails(template_name="footer", template_params={}) |
| 73 | + |
| 74 | + return EmailParams(email_templates=[body, footer], **email_kwargs) |
| 75 | + |
| 76 | + |
| 77 | +def send_power_testing_email( |
| 78 | + smtp_account: SMTPAccount, |
| 79 | + cloud_account: Union[CloudDomains, str], |
| 80 | + flavor_name_list: List[str], |
| 81 | + limit_by_projects: Optional[List[str]] = None, |
| 82 | + all_projects: bool = False, |
| 83 | + as_html: bool = False, |
| 84 | + send_email: bool = False, |
| 85 | + use_override: bool = False, |
| 86 | + override_email_address: Optional[str] = "cloud-support@stfc.ac.uk", |
| 87 | + cc_cloud_support: bool = False, |
| 88 | + **email_params_kwargs, |
| 89 | +): |
| 90 | + """ |
| 91 | + Notify users by email if they own VMs using flavors scheduled for power testing. |
| 92 | +
|
| 93 | + Each user receives a personalized email listing: |
| 94 | + - Flavors under review |
| 95 | + - Planned testing dates |
| 96 | + - Affected VMs they own |
| 97 | +
|
| 98 | + :param smtp_account: SMTP configuration used to send the email. |
| 99 | + :param cloud_account: Name of the OpenStack account (from clouds.yaml) to authenticate with. |
| 100 | + :param flavor_name_list: List of flavor names that are under power testing or EOL consideration. |
| 101 | + :param limit_by_projects: (Optional) List of projects to scope the search to (mutually exclusive with all_projects). |
| 102 | + :param all_projects: If True, search all OpenStack projects for affected VMs. |
| 103 | + :param as_html: If True, emails are formatted as HTML; otherwise, plain text is used. |
| 104 | + :param send_email: If True, emails are actually sent; otherwise, the generated content is printed. |
| 105 | + :param use_override: If True, all emails are redirected to the override email address. |
| 106 | + :param override_email_address: Email address to use if override is enabled or if user's address is not found. |
| 107 | + :param cc_cloud_support: If True, cc cloud-support@stfc.ac.uk on all outgoing emails. |
| 108 | + :param email_params_kwargs: Additional arguments passed to EmailParams, such as subject or sender. |
| 109 | + """ |
| 110 | + validate_input_arguments(flavor_name_list, limit_by_projects, all_projects) |
| 111 | + |
| 112 | + server_query = find_servers_with_decom_flavors( |
| 113 | + cloud_account, flavor_name_list, limit_by_projects |
| 114 | + ) |
| 115 | + |
| 116 | + for user_id in server_query.to_props().keys(): |
| 117 | + # if email_address not found - send to override_email_address |
| 118 | + # also send to override_email_address if override_email set |
| 119 | + user_name, email_addr = find_user_info( |
| 120 | + user_id, cloud_account, override_email_address |
| 121 | + ) |
| 122 | + send_to = [email_addr] |
| 123 | + if use_override: |
| 124 | + send_to = [override_email_address] |
| 125 | + |
| 126 | + if as_html: |
| 127 | + affected_flavors_table = tabulate( |
| 128 | + [{"Flavor": flavor} for flavor in flavor_name_list], |
| 129 | + headers="keys", |
| 130 | + tablefmt="html", |
| 131 | + ) |
| 132 | + else: |
| 133 | + affected_flavors_table = tabulate( |
| 134 | + [{"Flavor": flavor} for flavor in flavor_name_list], |
| 135 | + headers="keys", |
| 136 | + tablefmt="grid", |
| 137 | + ) |
| 138 | + email_params = build_email_params( |
| 139 | + user_name=user_name, |
| 140 | + affected_flavors_table=affected_flavors_table, |
| 141 | + affected_servers_table=( |
| 142 | + server_query.to_string(groups=[user_id]) |
| 143 | + if not as_html |
| 144 | + else server_query.to_html(groups=[user_id]) |
| 145 | + ), |
| 146 | + email_to=send_to, |
| 147 | + as_html=as_html, |
| 148 | + email_cc=("cloud-support@stfc.ac.uk",) if cc_cloud_support else None, |
| 149 | + **email_params_kwargs, |
| 150 | + ) |
| 151 | + |
| 152 | + if not send_email: |
| 153 | + Emailer(smtp_account).print_email(email_params) |
| 154 | + |
| 155 | + else: |
| 156 | + Emailer(smtp_account).send_emails([email_params]) |
0 commit comments