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-northeast-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): if line.startswith("## 演習 "): paneId = "e" + line.replace("## 演習 ", "").strip() paneIds += [paneId] 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 = ( '
  • ' + "手順 " + 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( '

    ' ) for pl in parsed_lines: if pl.startswith("

    概要"): if len(nodes_table) > 50: print(nodes_table) else: print( "

    をクリックし、カウントダウン タイマーが終了するまで約 6 分待ちます。完了すると、上部の詳細な演習タブのロックが解除され、演習環境の情報が表示されます。


    " ) elif pl.startswith("

    演習 "): overview_tab = False print( '' ) if len(nodes_table) > 50: print( '' ) else: print( "" ) if len(nodes_table) > 50: print( '

    演習 ", "").replace("

    ", "").strip() + '">
    ' ) else: print("
    ") break elif pl.startswith("

    注意"): if len(nodes_table) > 50: print( '' ) print( '' ) print( '



    ' ) print(pl) else: print("
    ") break else: if " 50: print(pl) else: None else: if "None" not in pd_dashboard_url: pl = pl.replace( "TiDB ダッシュボード", 'TiDB ダッシュボード', ) if grafana_dashboard_url != None: pl = pl.replace( "Grafana ダッシュボード", 'Grafana ダッシュボード', ) if len(nodes_table) > 50: print( pl.replace("monitor1_address", m_public_ip) .replace("trainer_name", trainer_name) .replace("us-west-2", "ap-northeast-1") .replace("oregon", "tokyo") ) else: print( pl.replace("us-west-2", "ap-northeast-1").replace( "oregon", "tokyo" ) ) if len(nodes_table) > 50: print( '' ) print( "

    をクリックして、演習リソースを破棄します。

    " ) # For terminate resources print("
    ") # For tab closing 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 = "セットアップが進行中 ..." 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, "
    インスタンス IDパブリック IPプライベート IP推奨役割講師タグ受講者タグ名前稼働時間(分)
    " 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: None if m_public_ip != "monitor1_address": nodes_table += "

    " if "None" not in pd_dashboard_url: nodes_table += ( '
    TiDB ダッシュボード - ' + 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 ダッシュボード - 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", "japanese", "16", pd_dashboard_url, )