import boto3, sys, datetime, socket, re def _get_boto_session(profile_name: str) -> boto3.Session: """ Get the boto3 session. Works with profile configured in runtime. :param profile_name: The configured AWS profile name. Pass in None to use default credentials chain. :returns: The Boto session. """ if profile_name == None: session = boto3.Session(region_name="ap-southeast-1") return session else: session = boto3.Session(profile_name=profile_name) return session def _parse_one_image(line: str) -> str: if re.match("^ *!\[", line): percent = re.sub("^ *!\[", "", line.split("](")[0]) return ( '' ) else: return line def _parse_one_href(line: str) -> str: if not re.match("^ *!\[", line) and re.match("^.*\[.*\]\(.*\)", line): anchor = ( '' + re.sub("^.*\[", "", line.split("](")[0]) + "" ) return re.sub("\[.*\)", anchor, line) else: return line def _parse_one_code_pv1(line: str) -> str: ret_tab = "

" + (" " * 17) if re.match("^.*`.*`", line): code = ( '' + line.split("`")[1].strip() + "" ) if ">Step" in line: return re.sub("`.*`", ret_tab + code, line) else: return re.sub("`.*`", code, line) else: return line def _parse_one_code(line: str, pos: int) -> str: if re.match("^.*`", line) and not re.match("^ *```", line): new_pos = pos + 1 if pos % 2 == 0: return _parse_one_code(line.replace("`", "", 1), new_pos) else: return _parse_one_code( line.replace("`", '', 1), new_pos ) else: return line def _process_command_begin() -> str: command_begin = '

' return command_begin def _process_command_end() -> str: command_end = "

" return command_end def _process_command(line: str) -> str: return "

" + line.replace(" ", " ") + "

" def _process_sql_begin() -> str: sql_begin = '
' return sql_begin def _process_sql_end() -> str: sql_end = "

" return sql_end def _process_sql(line: str) -> str: # return "

" + line.replace(" ", " ") + "

" return line + "\n" def _process_output_begin() -> str: output_begin = '
' return output_begin def _process_output_end() -> str: output_end = "

" return output_end def _process_output(line: str) -> str: return "

" + line.replace(" ", " ") + "

" def _parse_and_print_md( normal_font_size: str, language: str, lab_guide_markdown_file: str, m_public_ip: str, trainer_name: str, pd_dashboard_url: str, grafana_dashboard_url: str, nodes_table: str, ): print("
") f = open( "/usr/share/httpd/lab_control_scripts/lib/" + language + "/" + lab_guide_markdown_file, "r", encoding="UTF-8", ) lines = f.read() f.close() parsed_lines = [] paneIds = ["overview"] in_sql = False in_command = False in_output = False for line in lines.splitlines(False): parsed_line = _parse_one_image(line) if in_command: if re.match("^ *``` *$", parsed_line): parsed_lines += [_process_command_end()] in_command = False else: parsed_lines += [_process_command(line)] elif in_output: if re.match("^ *``` *$", parsed_line): parsed_lines += [_process_output_end()] in_output = False else: parsed_lines += [_process_output(line)] elif in_sql: if re.match("^ *``` *$", parsed_line): parsed_lines += [_process_sql_end()] in_sql = False else: parsed_lines += [_process_sql(line)] elif re.match("^# ", line): title = ( '

' + parsed_line.replace("# ", "", 1).strip() + "


" ) # print(title) elif re.match("^### ", parsed_line): parsed_lines += [ "

" + parsed_line.replace("### ", "", 1).strip() + "

" ] elif re.match("^## ", parsed_line): parsed_lines += [ "

" + parsed_line.replace("## ", "", 1).strip() + "

" ] elif re.match("^\+ ", parsed_line): tl = ( '
  • ' + parsed_line.replace("+ ", "", 1).strip() + "
  • " ) ptl = _parse_one_code(_parse_one_href(tl), 1) parsed_lines += [ptl] elif re.match("^ +\+ ", parsed_line): tl = ( '
  • ' + parsed_line.replace("+ ", "", 1).strip() + "
  • " ) ptl = _parse_one_code(_parse_one_href(tl), 1) parsed_lines += [ptl] elif re.match("^[0-9]*\. ", parsed_line): tl = ( '
  • ' + "Step " + re.findall("^([0-9]+\.)", parsed_line)[0] + " " + re.sub("^[0-9]*\. ", "", parsed_line, 1).strip() + "

  • " ) ptl = _parse_one_code(_parse_one_href(tl), 1) parsed_lines += [ptl] elif re.match("^ *```command", parsed_line): parsed_lines += [_process_command_begin()] in_command = True elif re.match("^ *```output", parsed_line): parsed_lines += [_process_output_begin()] in_output = True elif re.match("^ *```sql", parsed_line): parsed_lines += [_process_sql_begin()] in_sql = True elif re.match("^ *``` *$", parsed_line): if in_command: parsed_lines += [_process_command_end()] in_command = False if in_output: parsed_lines += [_process_output_end()] in_output = False if in_sql: parsed_lines += [_process_sql_end()] in_sql = False else: ptl = ( '
    ' + _parse_one_code(_parse_one_href(parsed_line), 1) + "

    " ) parsed_lines += [ptl] print('
    ') print('") print( '

    ' ) single_nodes_table = True single_create_lab_hint = True for pl in parsed_lines: if pl.startswith("

    Overview") and single_nodes_table: if len(nodes_table) > 50: print(nodes_table) single_nodes_table = False elif single_create_lab_hint: print( "

    Click CREATE LAB and wait for the countdown timer to finish to access the lab resources


    " ) single_create_lab_hint = False elif pl.startswith("

    Exercise "): print( '' ) print( '' ) print( '

    Exercise ", "").replace("

    ", "").strip() + '">
    ' ) else: if "TiDB Dashboard', ) if grafana_dashboard_url != None: pl = pl.replace( "Grafana Dashboard", 'Grafana Dashboard', ) print( pl.replace("monitor1_address", m_public_ip) .replace("trainer_name", trainer_name) .replace("us-west-2", "ap-southeast-1") .replace("oregon", "singapore") ) print("
    ") # For tab closing # print( # '
    ' # ) # print("   ") # print( # '' # ) print("
    ") # For panel closing print("
    ") # For routine section closing def _check( session: boto3.Session, trainer_name: str, lab_guide_markdown_file: str, language: str, normal_font_size: str, pd_dashboard_url: str, ): grafana_dashboard_url = None step_2_needed = None nodes_table = "" # AWS pricing for SIN # c5_xlarge_instance_minute_price = 0.003266666666666667 # ebs_gp2_100gb_minute_price = 0.00023148148148148146 # AWS pricing for NRT # c5_xlarge_instance_minute_price = 0.0035666666666666668 # ebs_gp2_100gb_minute_price = 0.00023148148148148146 # AWS pricing for PAX c5_xlarge_instance_minute_price = 0.0028333333333333335 ebs_gp2_100gb_minute_price = 0.00023148148148148146 c5_xlarge_minute_price = ( c5_xlarge_instance_minute_price + ebs_gp2_100gb_minute_price ) ec2_client = session.client("ec2") sum_cost = 0.0 instances_dict = {} response = None real_reservations = [] if trainer_name == "all": response = ec2_client.describe_instances( Filters=[ { "Name": "instance-state-name", "Values": [ "running", ], }, ], DryRun=False, MaxResults=500, ) real_reservations += [response["Reservation"]] while "NextToken" in response and response["NextToken"] != None: nt = response["NextToken"] response = ec2_client.describe_instances( Filters=[ { "Name": "instance-state-name", "Values": [ "running", ], }, ], DryRun=False, MaxResults=500, NextToken=nt, ) real_reservations += [response["Reservation"]] else: response = ec2_client.describe_instances( Filters=[ { "Name": "instance-state-name", "Values": [ "running", ], }, { "Name": "tag:trainer", "Values": [ trainer_name, ], }, ], DryRun=False, MaxResults=500, ) real_reservations += [response["Reservations"]] while "NextToken" in response and response["NextToken"] != None: nt = response["NextToken"] response = ec2_client.describe_instances( Filters=[ { "Name": "instance-state-name", "Values": [ "running", ], }, { "Name": "tag:trainer", "Values": [ trainer_name, ], }, ], DryRun=False, MaxResults=500, NextToken=nt, ) real_reservations += [response["Reservations"]] for reservations in real_reservations: for reservation in reservations: for instances in reservation["Instances"]: up_time = round( ( datetime.datetime.now(datetime.timezone.utc) - instances["UsageOperationUpdateTime"] ).seconds / 60 ) name = None trainer = None student = None node_role = None for tag in instances["Tags"]: if tag["Key"] == "Name": name = tag["Value"] if tag["Key"] == "trainer": trainer = tag["Value"] if tag["Key"] == "student": student = tag["Value"] if tag["Key"] == "role": node_role = tag["Value"] if ( name == None or trainer == None or student == None or node_role == None ) and (name != "lab-control"): step_2_needed = "Setup Phase 2 In-Progress ..." cost = 0.0 cost = round(up_time * c5_xlarge_minute_price, 2) if name != "lab-control": sum_cost += cost instances_dict.update( { (trainer if trainer != None else " ") + ":" + (student if student != None else " ") + ":" + (node_role if node_role != None else " ") + ":" + instances["InstanceId"]: "" + instances["InstanceId"].rjust(19, " ") + "" + instances["PublicIpAddress"].ljust(16, " ") + "" + instances["PrivateIpAddress"].ljust(16, " ") + "" + (name.ljust(13, " ") if name != None else " ") + "" + (trainer.ljust(10, " ") if trainer != None else " ") + "" + (student.ljust(7, " ") if student != None else " ") + "" + (node_role.ljust(9, " ") if node_role != None else " ") + "" + str(up_time).ljust(14, " ") + "" } ) else: cost = str(cost) + " (covered by RI)" if step_2_needed != None: print("

    " + step_2_needed + "

    ") trainer_name_cache = "NONE" student_name_cache = "NONE" for key in sorted([*instances_dict]): trainer_name = key.split(":")[0] student_name = key.split(":")[1] if trainer_name != trainer_name_cache or student_name != student_name_cache: nodes_table += "" # nodes_table += ("") nodes_table += "" nodes_table += "" nodes_table += "" nodes_table += "" trainer_name_cache = trainer_name student_name_cache = student_name nodes_table += instances_dict[key] nodes_table += "" nodes_table += "
    Available Nodes for", trainer_name, "
    Instance IDPublic IPPrivate IPSuggested RoleInstructorStudentNameUp Time (mins)
    " m_public_ip = "monitor1_address" for key in sorted([*instances_dict]): trainer_name = key.split(":")[0] student_name = key.split(":")[1] trainer_name_cache = trainer_name student_name_cache = student_name row = instances_dict[key] if "monitor1" in row: m_public_ip = row.split("")[1].strip() if len([*instances_dict]) > 0: nodes_table += ( "

    Reporting at - (UTC) " + str(datetime.datetime.now()) + " with " + str(len([*instances_dict])) + " nodes.

    " ) if m_public_ip != "monitor1_address": nodes_table += "

    " if "None" not in pd_dashboard_url: nodes_table += ( 'TiDB Dashboard - ' + pd_dashboard_url + "

    " ) monitor_ip = m_public_ip.strip() grafana_port = 3000 timeout_seconds = 1 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout_seconds) result = sock.connect_ex((monitor_ip, int(grafana_port))) if result == 0: grafana_dashboard_url = "http://" + monitor_ip + ":3000" nodes_table += ( 'Grafana Dashboard - http://' + monitor_ip + ":3000" ) sock.close() nodes_table += "


    " else: None # print("") _parse_and_print_md( normal_font_size, language, lab_guide_markdown_file, m_public_ip, trainer_name, pd_dashboard_url, grafana_dashboard_url, nodes_table, ) if __name__ == "__main__": trainer_name = sys.argv[1] pd_dashboard_url = sys.argv[2] lab_name = sys.argv[3] session = _get_boto_session(None) _check( session, trainer_name, lab_name + "-lab-guide.md", "english", "16", pd_dashboard_url, )