やる気を削がれる出来事

 アーマードコアⅥのデータが消し飛んだ。クラウドのデータも逝かれた。
今日PS5を久しぶりに起動しアーマードコアⅥをやろうとしたのだが、何故かニューゲームしかなくて、頭に疑問符を浮かべながらクラウドの同期確認をしたところ、クラウド側のデータが更新されてセーブデータが消し飛んだ。まるで意味が分からない。
 下手糞ながら3周してパーツを全収集していたのにお釈迦になった。
別に対戦したかった訳ではないのだが、武器やパーツを付け替えて勝てそうなアセンを考えたかったのに、まさか一からやり直す羽目になるとは思いもしなかった。
あまりの理不尽極まりない事態に正直かなり参っている。

ARMORED CORE™ VI FIRES OF RUBICON™_20251112164541

 スピリチュアル界隈だとここから何を学べたかという話になるのだろうが、正直なところここから学べることなんてあるだろうか?
 精神が鍛えられた?単にやる気を削がれたうえ、一からやり直すという保証もないのに精神が鍛えられるはずもない。単なる詭弁である。
 バックアップを取った方が良かった?娯楽にまで徹底した対策が必要なのであれば息が詰まる。
そんな生き方は誰も望まない。いったいどこで緊張を解けというのだろうか。。。
 話のネタになった?こんなつまらない話など一時だけの話題にしかならない。
そんなことのためにセーブデータが犠牲になったというなら糞喰らえである。

万が一にでも私の方に過失があったなら、貴重な学びを得たと胸を張って言えるが、過失無き理不尽は単なる理不尽である。私はそれを喜ぶようなマゾヒストではない。断じて違う。
例えば、通り魔に襲われて貴重な学びを得たと言う者はいない。仮にそんな経験をしても対策のしようが無い。防犯グッズを買っておけばよかったなんていうのはお門違いだ。
対策をしたところでそれが完璧に動作する保証はない、完璧に動作したところで有効である保証もない。トライ&エラーで有効な手段を探るわけにもいかない。そもそも、状況によって有効かどうかは変わる。そして、そう何度も起きるわけがないのだ。
 いつか必ず起きる災害と違って、通り魔には一生遭遇しないことも多い。
それなのにいつ如何なる時も様々な状況を想定して防犯対策を強いられるのはおかしな話だ。
膨大な可能性の海の中で、確率の低い事象にまで一々対策していられないのだ。

 そういえば、少し前にJICAが4都市でアフリカのホームタウン化を計画していたが、あれが実現すれば通り魔犯罪が日常になる可能性がある。よりによってアフリカでも有数の犯罪大国から受け入れようとするのはそこはかとなく悪意を感じる。恐らく恣意的なものだろう。偶然も重なれば必然なのだ。
 それにしても身体能力に秀でた外国人に襲われる恐怖は計り知れないものがある。
女性は特にそうだろう。気の毒なことだ。
 そういえば、熊の被害も増えているのだったか。それなんてディストピア。
動物は電磁波に敏感らしいので、あちらこちらに電磁波をまき散らされるのは参るのだろう。
事務所が難波にあった時は私も出社するたびに体調を崩したものだ。
当時は高所が駄目だと思っていたが、電磁波(特に5G)の影響だったかもしれない。
そういう可能性も微レ存。

created by Rinker
¥7,821 (2025/11/27 08:34:15時点 楽天市場調べ-詳細)

サーバー再起動処理

 さて、成果物の棚卸しの第一弾として、AWSのサーバー再起動のために組んだPythonコードを下記に添付する。
とはいえ、会社の情報が洩れそうな部分は伏字や抜粋していない。(子クラスなど)
あくまで掲載しても問題なさそうな部分だけである。
 対象のサーバーは冗長構成で構成されており、ロードバランサーからの切り離しや、EC2内で稼働しているソフトウェア(ジョブ管理ソフト(JobCenterやSystemwalker)など)を個別に切り離す必要があった。(役割によっても切り離し手順が異なる。)
ロードバランサーについては、切り離しに5分間の待機が必要だったためLambdaの稼働時間はかなりぎりぎりだったのを覚えている。
結局AWS側のリソース不足でサーバーが再稼働出来ない不具合がしばしば起きたため、利用が停止された。今回はその供養も兼ねている。使えそうな部分があればぜひ使ってやってほしい。
 raize の使用方法が間違っているといった点についてはご愛敬。
言い訳としては、実務での使用回数が数回程度しかなかったのと、そもそもプログラミングをする機会が年に1回あればいい方だったので細かい文法は毎回覚え直しだったのだ。。。
仕事では主にLinuxOSのメンテナンスの対応ばかりだった。
お陰でLinuxOSのアップグレード作業という貴重な体験もさせてもらえたが。。。

import boto3

import time
from datetime import datetime, timedelta, timezone


# 関数名 : lambda_handler
# 機能   : ハンドラー関数(エントリポイントとして機能)
def lambda_handler(event, context):
    # ====== パラメータ設定 ==================================
    # タイムスタンプを取得
    print('StartTime:' + get_timestamp())
    print('Start ServerReboot')
    
    sv_reboot = RebootXXXXX()    # ServerRebootの継承クラス
    
    #sv_reboot.detach_nlb()
    #sv_reboot.attach_nlb()
    if not sv_reboot.exec():
        print('Faield to ServerReboot')
        raise 'Failed to ServerReboot'
    
    print('End ServerReboot')
    print('EndTime:' + get_timestamp())
    
    # 正常終了
    return 'End ServerReboot'


# 関数名 : get_timestamp
# 戻り値 : 文字列(str)
# 引数   : datefmt(str)
# 機能   : タイムスタンプを取得
def get_timestamp(datefmt = '%Y-%m-%dT%H:%M:%S.%fZ'):
    # タイムゾーンの生成
    JST = timezone(timedelta(hours = +9), 'JST')
    
    # タイムスタンプを取得
    return datetime.now(JST).strftime(datefmt)


# 関数名 : create_filter
# 戻り値 : リスト(list)
# 引数   : filter(str)
#        : values(list)
# 機能   : フィルターを作成
#        : valuesはリスト
def create_filter(filter, values):
    filters = [
        {
            'Name': filter,
            'Values': values
        }
    ]
    
    return filters


# 関数名 : create_filter_and
# 戻り値 : リスト(list)
# 引数   : filter1(str)
#        : values1(list)
#        : filter2(str)
#        : values2(list)
# 機能   : AND条件のフィルターを作成
#        : values1、values2はリスト
def create_filter_and(filter1, values1, filter2, values2):
    filters = [
        {
            'Name': filter1,
            'Values': values1
        },
        {
            'Name': filter2,
            'Values': values2
        }
    ]
    
    return filters



# SNS操作クラス
class SNSAction:
    # 関数名 : __init__
    # 戻り値 : None
    # 引数   : None
    # 機能   : コンストラクタ
    def __init__(self):
        # SNSのインスタンスを生成
        self.sns = boto3.client('sns', region_name = 'ap-northeast-1')


    # 関数名 : send_message
    # 戻り値 : None
    # 引数   : topic_arn(str)
    #        : subject(str)
    #        : send_message(list)
    # 機能   : AWS/SNSの機能を使用しメール通知を行う
    def send_message(self, topic_arn, subject, send_message):
        try:
            # AWS/SNSでメールを送信。
            response = self.sns.publish(
                TopicArn = topic_arn,     # AWS/SNSのトピックARNをセット
                Message = send_message,   # 送信メッセージをセット
                Subject = subject         # 件名をセット
            )
            messageid = response['MessageId']
            print('MessageId:' + messageid)
            
        except Exception as ex:
            # メール送信に失敗した場合、エラーを発生させる。
            print('Failed to SendMessage')
            print(ex)
            raise ex
    
    
    # 関数名 : create_topic
    # 戻り値 : 文字列(str)
    # 引数   : topic_name(str)
    # 機能   : トピック名を引数にトピックARNを取得する
    def create_topic(self, topic_name):
        response = ''
        try:
            response = self.sns.create_topic(Name = topic_name)['TopicArn']    # 設定したトピック名からトピックARNを取得
        except Exception as ex:
            # トピックARNの取得に失敗した場合、エラーを発生される。
            print('Failed to CreateTopic')
            print(ex)
            raise ex
        return response



# SSM操作クラス
class SSMAction:
    # メンバ変数定義
    __ssmcmd_status = ''    # SSMコマンドのステータス
    __ssmcmd_output = ''    # SSMコマンドの実行結果
    __ssmcmd_exitcode = ''  # SSMコマンドの終了コード
    
    
    # 関数名 : __init__
    # 戻り値 : None
    # 引数   : None
    # 機能   : コンストラクタ
    def __init__(self):
        # SSMのインスタンスを生成
        self.ssm = boto3.client('ssm', region_name = 'ap-northeast-1')
    
    
    # 関数名 : describe_instance_information
    # 戻り値 : 真偽値(True:実行可能、False:実行不可)
    # 引数   : instanceids(list)
    # 機能   : SSMが実行可能状態かチェック
    def describe_instance_information(self, instanceids):
        try:
            response = self.ssm.describe_instance_information(
                InstanceInformationFilterList = [
                    {
                        'key': 'InstanceIds',
                        'valueSet': instanceids
                    }])
            
            #print(response)
            if len(response['InstanceInformationList']) == 0:
                return False
        except Exception as ex:
            # 異常終了
            print('Failed to SSM DescribeInstancesInformation')
            print(ex)
            raise ex
        return True


    # 関数名 : send_command
    # 戻り値 : 文字列(str)
    # 引数   : instanceids(list)
    #        : commands(list)
    #        : timeoutsec(integer)
    # 機能   : SSMにコマンドを送信する
    def send_command(self, instanceids, commands, timeoutsec = 60):
        commandid = ''
        try:
            # ssmにコマンドを送信
            response = self.ssm.send_command(
                InstanceIds = instanceids,              #コマンドを送信するEC2インスタンスID
                DocumentName = 'AWS-RunShellScript',    #AWS/CLIを使用
                TimeoutSeconds = timeoutsec,            #処理開始から1分後にタイムアウト
                Parameters = {
                    'commands': commands                #CLIコマンドをセット
                })
                
            if len(response) > 0:
                #実行結果を確認するため、CommandIdを取得
                commandid = response['Command']['CommandId']
            
        except Exception as ex:
            # 異常終了
            print('Failed to SSM SendCommand')
            print(ex)
            raise ex
        return str(commandid)


    # 関数名 : list_command_invocations
    # 戻り値 : 辞書型(dict)
    # 引数   : commandid(str)
    #        : details(Boolean)
    # 機能   : SSMコマンドのレスポンスを取得
    def list_command_invocations(self, commandid, details = True):
        response = {}
        try:
            # SSMに送信したコマンドの実行結果を取得する。
            response = self.ssm.list_command_invocations(
                CommandId = commandid,    # コマンドIDをセット
                Details = details)        # 詳細情報を取得する
        except Exception as ex:
            # 異常終了
            print('Failed to SSM ListCommandInvocations')
            print(ex)
            raise ex
        return response
    
    
    # 関数名 : list_command_invocations
    # 戻り値 : 辞書型(dict)
    # 引数   : commandid(str)
    #        : filters(list)
    #        : details(Boolean)
    # 機能   : SSMコマンドのレスポンスを取得
    #def list_command_invocations(self, commandid, filters, details = True):
    #    response = {}
    #    try:
    #        # SSMに送信したコマンドの実行結果を取得する。
    #        response = self.ssm.list_command_invocations(
    #            CommandId = commandid,    # コマンドIDをセット
    #            Filters = filters,        # フィルターをセット
    #            Details = details)        # 詳細情報を取得する
    #    except Exception as ex:
    #        # 異常終了
    #        print('Failed to SSM ListCommandInvocations')
    #        print(ex)
    #        raise ex
    #    return response


    # 関数名 : get_ssmcmd_results
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : commandid(str)
    #        : interval(float)
    #        : maxattempts(integer)
    # 機能   : SSMコマンドの実行結果を取得
    def get_ssmcmd_results(self, commandid, interval, maxattempts = 60):
        cnt_attempt = 0
        
        #実行結果を取得できるまでループ
        while True:
            cnt_attempt += 1
            if cnt_attempt > maxattempts:
                # タイムアウト
                self.clear_result()
                print('Failed to SSM Command Timeout')
                return False
            
            #SSMコマンドの実行結果を取得
            response = self.list_command_invocations(commandid)
            
            #print(response)
            #実行結果の詳細情報が空なら待ち
            if len(response['CommandInvocations']) == 0:
                print('list_command_invocations waiting')
                
                #一定時間スリープ
                time.sleep(interval)
                continue
            
            status = response['CommandInvocations'][0]['Status']    #コマンドのステータスを取得
            
            # SSMコマンドが実行中ではないなら実行結果を取得する
            if not self.ssmcmd_status_waiting(status):
                self.__ssmcmd_exitcode = response['CommandInvocations'][0]['CommandPlugins'][0]['ResponseCode']    #コマンドの終了コードを取得
                self.__ssmcmd_output = response['CommandInvocations'][0]['CommandPlugins'][0]['Output']           #コマンドの実行結果を取得
                self.__ssmcmd_status = status   # 実行結果のステータスを記録する
                
                #コマンドの実行結果が Success(成功) 以外なら
                if not self.ssmcmd_status_success(self.__ssmcmd_status):
                    # 異常終了
                    return False
                
                #実行結果取得後、ループを抜ける
                break
            else:
                #一定時間スリープ
                print('waiting:' + status)
                time.sleep(interval)
        
        # 正常終了
        return True


    # 関数名 : ssmcmd_status_success
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : status(str)
    # 機能   : SSMコマンドの実行結果がSuccess(成功)かチェック
    def ssmcmd_status_success(self, status):
        # Success:成功以外のステータスの場合はFalse
        if status == 'Success':
            return True
        return False
    
    
    # 関数名 : ssmcmd_status_waiting
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : status(str)
    # 機能   : SSMコマンドの実行結果がPending(待ち状態)、もしくはInProgress(処理中)かチェック
    def ssmcmd_status_waiting(self, status):
        # ステータスがPending(待ち状態)、もしくはInProgress(処理中)の場合はTrue
        if status == 'Pending': return True
        if status == 'InProgress': return True
        return False
    
    
    # 関数名 : get_ssmcmd_exitcode
    # 戻り値 : 数値(integer)
    # 引数   : None
    # 機能   : SSMコマンドの終了コードを返す
    def get_ssmcmd_exitcode(self):
        return self.__ssmcmd_exitcode
    
    
    # 関数名 : get_ssmcmd_output
    # 戻り値 : 文字列(str)
    # 引数   : None
    # 機能   : SSMコマンドの実行結果を返す
    def get_ssmcmd_output(self):
        return self.__ssmcmd_output
    
    
    # 関数名 : get_ssmcmd_status
    # 戻り値 : 文字列(str)
    # 引数   : None
    # 機能   : SSMコマンドのステータスを返す
    def get_ssmcmd_status(self):
        return self.__ssmcmd_status
    
    
    # 関数名 : clear_result
    # 戻り値 : None
    # 引数   : None
    # 機能   : 出力結果をクリア
    def clear_result(self):
        self.__ssmcmd_status = ''    # SSMコマンドのステータス
        self.__ssmcmd_output = ''    # SSMコマンドの実行結果
        self.__ssmcmd_exitcode = ''  # SSMコマンドの終了コード
    


# EC2操作クラス
class EC2Action:
    # 関数名 : __init__
    # 戻り値 : None
    # 引数   : None
    # 機能   : コンストラクタ
    def __init__(self):
        # EC2のインスタンスを生成
        self.ec2 = boto3.client('ec2', region_name = 'ap-northeast-1')
    
    
    # 関数名 : reboot_instances
    # 戻り値 : なし
    # 引数   : instanceids(list)
    # 機能   : EC2を再起動する
    def reboot_instances(self, instanceids):
        try:
            # ssmにコマンドを送信
            self.ec2.reboot_instances(InstanceIds = instanceids, DryRun = False)
            
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 RebootInstances')
            print(ex)
            raise ex
    
    
    # 関数名 : stop_instances
    # 戻り値 : なし
    # 引数   : instanceids(list)
    # 機能   : EC2を停止する
    def stop_instances(self, instanceids):
        try:
            # ssmにコマンドを送信
            self.ec2.stop_instances(InstanceIds = instanceids, DryRun = False)
            
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 StopInstances')
            print(ex)
            raise ex
    
    
    # 関数名 : start_instances
    # 戻り値 : なし
    # 引数   : instanceids(list)
    # 機能   : EC2を起動する
    def start_instances(self, instanceids):
        try:
            # ssmにコマンドを送信
            self.ec2.start_instances(InstanceIds = instanceids, DryRun = False)
            
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 StartInstances')
            print(ex)
            raise ex
    
    
    # 関数名 : get_ec2_instanceid
    # 戻り値 : 文字列(str)
    # 引数   : filters(list)
    # 機能   : EC2を再起動する
    def get_ec2_instanceid(self, filters):
        response = []
        try:
            response = self.ec2.describe_instances(Filters = filters)
        except Exception as ex:
            print('Failed to EC2 DescribeInstances')
            print(ex)
            raise ex
        return response['Reservations'][0]['Instances'][0]['InstanceId']


    # 関数名 : wait_instance_stopped
    # 戻り値 : なし
    # 引数   : instanceids(list)
    #        : delay(integer)
    #        : maxattempts(integer)
    # 機能   : EC2を再起動する
    def wait_instance_stopped(self, instanceids, delay = 15, maxattempts = 40):
        try:
            # Waiterを生成
            waiter = self.ec2.get_waiter('instance_stopped')
            
            # EC2のインスタンスが起動するまで待ち
            waiter.wait(
                Filters = [],
                InstanceIds = instanceids,
                DryRun = False,
                #MaxResults = 5,
                #NextToken = '',
                WaiterConfig = {
                    'Delay': delay,              # 指定した間隔でポーリング
                    'MaxAttempts': maxattempts   # 指定した回数ポーリング試行
                })
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 Waiter InstanceStopped')
            print(ex)
            raise ex
    

    # 関数名 : wait_instance_running
    # 戻り値 : なし
    # 引数   : instanceids(list)
    #        : delay(integer)
    #        : maxattempts(integer)
    # 機能   : EC2を再起動する
    def wait_instance_running(self, instanceids, delay = 15, maxattempts = 40):
        try:
            # Waiterを生成
            waiter = self.ec2.get_waiter('instance_running')
            
            # EC2のインスタンスが起動するまで待ち
            waiter.wait(
                Filters = [],
                InstanceIds = instanceids,
                DryRun = False,
                #MaxResults = 5,
                #NextToken = '',
                WaiterConfig = {
                    'Delay': delay,              # 指定した間隔でポーリング
                    'MaxAttempts': maxattempts   # 指定した回数ポーリング試行
                })
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 Waiter InstanceRunning')
            print(ex)
            raise ex


# 関数名 : wait_instance_status_ok
    # 戻り値 : なし
    # 引数   : instanceids(list)
    #        : delay(integer)
    #        : maxattempts(integer)
    # 機能   : EC2を再起動する
    def wait_instance_status_ok(self, instanceids, allinstances = True, delay = 15, maxattempts = 40):
        try:
            # Waiterを生成
            waiter = self.ec2.get_waiter('instance_status_ok')
            
            # EC2のインスタンスが起動するまで待ち
            waiter.wait(
                Filters = [],
                InstanceIds = instanceids,
                DryRun = False,
                #MaxResults = 5,
                #NextToken = '',
                IncludeAllInstances = allinstances, # ステータスがRunning以外のインスタンスも確認
                WaiterConfig = {
                    'Delay': delay,              # 指定した間隔でポーリング
                    'MaxAttempts': maxattempts   # 指定した回数ポーリング試行
                })
        except Exception as ex:
            # 異常終了
            print('Failed to EC2 Waiter InstanceRunning')
            print(ex)
            raise ex


# ElasticLoadBalancingV2操作クラス
class ELBV2Action:
    # 関数名 : __init__
    # 戻り値 : None
    # 引数   : None
    # 機能   : コンストラクタ
    def __init__(self):
        # ElasticLoadBalancingV2のインスタンスを生成
        self.elbv2 = boto3.client('elbv2', region_name = 'ap-northeast-1')
    
    
    # 関数名 : register_targets
    # 戻り値 : なし
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    #        : port(integer)
    #        : az(str)
    # 機能   : ELBのターゲットグループにEC2インスタンスIDを紐付け
    #        : ターゲットグループの種類が"instance"の場合は別の関数を使用
    #def register_targets(self, targetgrp_arn, ec2_instanceid, port, az = 'all'):
    #    try:
    #        self.elbv2.register_targets(
    #            TargetGroupArn = targetgrp_arn,     # ターゲットグループのARNリソース名
    #            Targets = [
    #                {
    #                    'Id': ec2_instanceid,       # EC2インスタンスID
    #                    'Port': port,               # ポート番号
    #                    'AvailabilityZone': az      # アベイラビリティーゾーン
    #                },
    #            ])
    #    except Exception as ex:
    #        print('Failed to ELBV2 RegisterTargets')
    #        print(ex)
    #        raise ex
    
    
    # 関数名 : register_targets
    # 戻り値 : なし
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    #        : port(integer)
    # 機能   : ELBのターゲットグループにEC2インスタンスIDを紐付け
    #        : ターゲットグループの種類が"instance"の場合はこの関数を使用
    def register_targets(self, targetgrp_arn, ec2_instanceid, port):
        try:
            self.elbv2.register_targets(
                TargetGroupArn = targetgrp_arn,     # ターゲットグループのARNリソース名
                Targets = [
                    {
                        'Id': ec2_instanceid,       # EC2インスタンスID
                        'Port': port                # ポート番号
                    },
                ])
        except Exception as ex:
            print('Failed to ELBV2 RegisterTargets')
            print(ex)
            raise ex
    
    
    # 関数名 : deregister_targets
    # 戻り値 : なし
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    #        : port(integer)
    #        : az(str)
    # 機能   : ELBのターゲットグループからEC2インスタンスIDを削除
    #        : ターゲットグループの種類が"instance"の場合は別の関数を使用
    #def deregister_targets(self, targetgrp_arn, ec2_instanceid, port, az = 'all'):
    #    try:
    #        self.elbv2.deregister_targets(
    #            TargetGroupArn = targetgrp_arn,     # ターゲットグループのARNリソース名
    #            Targets = [
    #                {
    #                    'Id': ec2_instanceid,       # EC2インスタンスID
    #                    'Port': port,               # ポート番号
    #                    'AvailabilityZone': az      # アベイラビリティーゾーン
    #                },
    #            ])
    #    except Exception as ex:
    #        print('Failed to ELBV2 RegisterTargets')
    #        print(ex)
    #        raise ex
    
    
    # 関数名 : deregister_targets
    # 戻り値 : なし
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    #        : port(integer)
    # 機能   : ELBのターゲットグループからEC2インスタンスIDを削除
    #        : ターゲットグループの種類が"instance"の場合はこの関数を使用
    def deregister_targets(self, targetgrp_arn, ec2_instanceid, port):
        try:
            self.elbv2.deregister_targets(
                TargetGroupArn = targetgrp_arn,     # ターゲットグループのARNリソース名
                Targets = [
                    {
                        'Id': ec2_instanceid,       # EC2インスタンスID
                        'Port': port                # ポート番号
                    },
                ])
        except Exception as ex:
            print('Failed to ELBV2 RegisterTargets')
            print(ex)
            raise ex
    
    
    # 関数名 : wait_target_deregistered
    # 戻り値 : なし
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    #        : port(integer)
    #        : delay(integer)
    #        : maxattempts(integer)
    # 機能   : ELBのターゲットグループからEC2インスタンスIDを削除されるまで待機
    def wait_target_deregistered(self, targetgrp_arn, ec2_instanceid, port, delay = 15, maxattempts = 40):
        try:
            # Waiterを生成
            waiter = self.elbv2.get_waiter('target_deregistered')
            
            # EC2のインスタンスが起動するまで待ち
            waiter.wait(
                TargetGroupArn = targetgrp_arn,     # ターゲットグループのARNリソース名
                Targets = [
                    {
                        'Id': ec2_instanceid,       # EC2インスタンスID
                        'Port': port                # ポート番号
                    },
                ],
                WaiterConfig = {
                    'Delay': delay,              # 指定した間隔でポーリング
                    'MaxAttempts': maxattempts   # 指定した回数ポーリング試行
                })
        except Exception as ex:
            # 異常終了
            print('Failed to ELBV2 Waiter TargetDeregistered')
            print(ex)
            raise ex
    

    # 関数名 : describe_load_balancers
    # 戻り値 : 辞書型(dict)
    # 引数   : elb_name(str)
    # 機能   : ロードバランサー名を元にロードバランサーの情報を取得
    def describe_load_balancers(self, elb_names):
        response = {}
        try:
            response = self.elbv2.describe_load_balancers(
                Names = elb_names
                )
        except Exception as ex:
            print('Failed to ELBV2 DescribeLoadBalancers')
            print(ex)
            raise ex
        
        return response
    
    
    # 関数名 : get_load_balancer_arn
    # 戻り値 : リスト(list)
    # 引数   : elb_name(str)
    # 機能   : ロードバランサー名を元にロードバランサーの情報を取得
    def get_load_balancer_arn(self, elb_names):
        response = self.describe_load_balancers(elb_names)
        
        #arns = []
        #for loadbalancer in response['LoadBalancers']
        #    arns.append(loadbalancer['LoadBalancerArn'])
            
        # ロードバランサーARNを取得
        #return arns
        return response['LoadBalancers'][0]['LoadBalancerArn']
    
    
    # 関数名 : describe_target_groups
    # 戻り値 : 辞書型(dict)
    # 引数   : elb_arn(str)
    # 機能   : ロードバランサーARNを元にターゲットグループの情報を取得
    def describe_target_groups(self, elb_arn):
        response = {}
        try:
            response = self.elbv2.describe_target_groups(LoadBalancerArn = elb_arn)
        except Exception as ex:
            print('Failed to ELBV2 DescribeTargetGroups')
            print(ex)
            raise ex
        
        return response
    
    
    # 関数名 : describe_target_health
    # 戻り値 : 辞書型(dict)
    # 引数   : targetgrp_arn(str)
    #        : ec2_instanceid(str)
    # 機能   : ターゲットグループARNとEC2インスタンスIDを元にヘルスチェック対象の情報を取得
    #        : ターゲットグループの種類が"instance"の場合はこの関数を使用
    def describe_target_health(self, targetgrp_arn, ec2_instanceid):
        response = {}
        try:
            response = self.elbv2.describe_target_health(
                TargetGroupArn = targetgrp_arn,
                Tragets = [
                    {
                        'Id': ec2_instanceid
                    }])
        except Exception as ex:
            print('Failed to ELBV2 DescribeTargetHealth')
            print(ex)
            raise ex
        
        return response



# 親クラス
class ServerReboot:
    # Lambda関数名
    functionname = ''
    
    # リブート対象のEC2インスタンスID
    target_instanceid = ''
    # リブート対象のEC2インスタンス名
    target_instancename = ''
    
    
    # 関数名 : __init__
    # 戻り値 : None
    # 引数   : None
    # 機能   : コンストラクタ
    def __init__(self):
        # 各リソース操作クラスの生成
        self.ec2_action = EC2Action()
        self.ssm_action = SSMAction()
        self.sns_action = SNSAction()
        self.elbv2_action = ELBV2Action()
    
    
    # 関数名 : lambda_lock
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : None
    # 機能   : 対象EC2の/tmpフォルダにロックファイルを作成し、Lambdaの冪統制を確保する
    def lambda_lock(self):
        # ====== Lambdaロック処理 =========================================
        # SSMコマンドが実行できる環境でないなら終了
        if not self.check_ssm_agent([self.target_instanceid]):
            # 異常終了
            return False
        
        # SSMコマンドの生成
        commands = [
            '/RDR/BC/unyo/shell/lambda_lock.sh ' + self.functionname
        ]

        print('Start Lambda Lock')

        # EDIAP1SVに対し再起動前処理を送信
        commandid = self.ssm_action.send_command([self.target_instanceid], commands)
    
        print('End Lambda Lock')
        
        #コマンドIDを表示
        print('CommandId:' + commandid)
        interval = 1.0
        
        # 終了コードが0(正常終了)ならTrueを返す
        return self.check_ssmcmd_exitcode(commandid, interval)
    
    
    # 関数名 : lambda_unlock
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : None
    # 機能   : 対象EC2の/tmpフォルダにロックファイルを作成し、Lambdaの冪統制を確保する
    def lambda_unlock(self):
        # ====== Lambdaロック処理 =========================================
        # SSMコマンドが実行できる環境でないなら終了
        if not self.check_ssm_agent([self.target_instanceid]):
            # 異常終了
            return False
        
        # SSMコマンドの生成
        commands = [
            '/RDR/BC/unyo/shell/lambda_unlock.sh ' + self.functionname
        ]

        print('Start Lambda Unlock')

        # EDIAP1SVに対し再起動前処理を送信
        commandid = self.ssm_action.send_command([self.target_instanceid], commands)
    
        print('End Lambda Unlock')
        
        #コマンドIDを表示
        print('CommandId:' + commandid)
        interval = 1.0
        
        # 終了コードが0(正常終了)ならTrueを返す
        return self.check_ssmcmd_exitcode(commandid, interval)
        
    
    # 関数名 : reboot
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2を再起動
    def reboot(self):
        try:
            #self.ec2_action.reboot_instances([self.target_instanceid])
            self.stop()
            
            self.wait_stopped()
            
            self.start()
        except Exception as ex:
            # 異常終了
            # 再起動に失敗した場合、各ソフトウェアを切り戻す
            print('Failed to EC2 RebortInstances')
            print(ex)
            self.attach()
            self.send_error_message('Failed to EC2 RebortInstances')
            return False
        return True
    
    
    # 関数名 : stop
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2を停止
    def stop(self):
        # EC2インスタンスを停止
        self.ec2_action.stop_instances([self.target_instanceid])
    
    
    # 関数名 : start
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2を起動
    def start(self):
        # EC2インスタンスを起動
        self.ec2_action.start_instances([self.target_instanceid])
    
    
    # 関数名 : wait
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2再起動まで待機
    def wait(self):
        try:
            # EC2インスタンスの再起動まで待機
            self.wait_status_ok()
            
            # SSMコマンドが実行可能になるまで待機
            self.wait_ssm_agent([self.target_instanceid])
            time.sleep(10)  # APEXが起動するまで待機
        except Exception as ex:
            # 異常終了
            # 起動待機処理に失敗した場合、各ソフトウェアを切り戻す
            print('Failed to EC2 Wait')
            print(ex)
            self.attach()
            self.send_error_message('Failed to EC2 Wait')
            return False
        return True
    
    
    # 関数名 : wait_ssm_agent
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : instanceids(list)
    #        : delay(integer)
    #        : maxattempts(integer)
    # 機能   : SSMコマンドが実行可能になるまで待機
    def wait_ssm_agent(self, instanceids, delay = 15, maxattempts = 40):
        for i in range(maxattempts):
            if self.check_ssm_agent(instanceids):
                # 正常終了
                return True
                
            # 一定時間スリープ
            time.sleep(delay)
        
        # 異常終了
        return False
    
    
    # 関数名 : check_ssm_agent
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : instanceids(list)
    # 機能   : SSMコマンドが実行可能か確認
    def check_ssm_agent(self, instanceids):
        return self.ssm_action.describe_instance_information(instanceids)
    
    
    # 関数名 : wait_stopped
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2停止まで待機
    def wait_stopped(self):
        # EC2インスタンスの停止まで待機
        self.ec2_action.wait_instance_stopped([self.target_instanceid])
    
    
    # 関数名 : wait_running
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2起動まで待機
    def wait_running(self):
        # EC2インスタンスの起動まで待機
        self.ec2_action.wait_instance_running([self.target_instanceid])
    
    
    # 関数名 : wait_status_ok
    # 戻り値 : None
    # 引数   : None
    # 機能   : EC2起動まで待機
    def wait_status_ok(self):
        # EC2インスタンスの起動まで待機
        self.ec2_action.wait_instance_status_ok([self.target_instanceid])
    
    
    # 関数名 : alive_monitoring
    # 戻り値 : None
    # 引数   : None
    # 機能   : 死活監視処理
    def alive_monitoring(self):
        return True
    
    
    # 関数名 : detach
    # 戻り値 : None
    # 引数   : None
    # 機能   : 切り離し処理
    def detach(self):
        return True
    
    
    # 関数名 : attach
    # 戻り値 : None
    # 引数   : None
    # 機能   : 切り戻し処理
    def attach(self):
        return True
    
    
    # 関数名 : server_reboot
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : None
    # 機能   : EC2を再起動
    def server_reboot(self):
        try:
            # ====== 死活監視処理開始 ==============================================
            if not self.alive_monitoring():
                # 異常終了
                return False
            
            
            # ====== 切り離し処理開始 ==============================================
            if not self.detach():
                # 異常終了
                return False
            
            
            # ====== EC2再起動処理開始 =============================================
            self.reboot()
            
            
            # ====== 起動待機処理開始 ============================================== 
            self.wait()
            
            
            # ====== 切り戻し処理開始 ==============================================
            if not self.attach():
                # 異常終了
                return False
            
        except Exception as ex:
            print('Failed to ' + self.functionname)
            self.send_error_message('Failed to ' + self.functionname)
            return False
        
        # 正常終了
        return True
    
    
    # 関数名 : exec
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : None
    # 機能   : 再起動の実行
    def exec(self):
        # ====== Lambda ロック処理開始 =========================================
        if not self.lambda_lock():
            print('Failed to Lambda Lock')
            # 異常終了
            return False
        
        
        # サーバー再起動を実行
        result = self.server_reboot()
        
        
        # ====== Lambda ロック解除処理開始 =========================================
        if not self.lambda_unlock():
            print('Failed to Lambda Unlock')
            # 異常終了
            return False
        
        return result
    
    
    # 関数名 : check_ssmcmd_exitcode
    # 戻り値 : 真偽値(True:正常終了、False:異常終了)
    # 引数   : commandid(str)
    #        : interval(integer)
    # 機能   : SSMコマンドの終了コードをチェック
    # 修正   : 試行回数を60から240の修正(2021/01/20)
    def check_ssmcmd_exitcode(self, commandid, interval, maxattempts = 240):
        print('Start Check SSMCommand Status')
        
        # SSMコマンドの実行結果を取得(True or False)
        response = self.ssm_action.get_ssmcmd_results(commandid, interval, maxattempts)
        
        output = self.ssm_action.get_ssmcmd_output()
        exitcode = self.ssm_action.get_ssmcmd_exitcode()
        status = self.ssm_action.get_ssmcmd_status()
        
        print('SSMCommand Status:' + status)
        print('SSMCommand ExitCode:' + str(exitcode))
        print(output)
        
        if not response:
            print('Failed to SSMCommand Status')
            return False
        
        # 終了コードの確認
        if exitcode == 0:
            # 正常終了
            print('End Check SSMCommand ExitCode')
            return True
        
        # 終了コードが不正な値
        print('Failed to SSMCommand ExitCode')
        return False


    # 関数名 : send_error_message
    # 戻り値 : None
    # 引数   : error_message(list)
    # 機能   : 障害通知を送信する。
    def send_error_message(self, error_message):
        # タイムスタンプを取得
        timestamp = get_timestamp()
        
        # 実行時には対象のトピック名に変更
        topic_name = 'SAMPLE-SSM-TOPIC'                             # メール送信を行うAWS/SNSのトピック名をセット
        topic_arn = self.sns_action.create_topic(topic_name)        # 設定したトピック名からトピックARNを取得
        
        subject = 'AWS/Lambda Error (' + self.functionname + ')'    # 固定値で件名をセット
        
        # 送信メッセージを作成
        send_message = timestamp + '\n\n'\
                + 'Lambda Error: ' + self.functionname + '\n'\
                + 'Instance Name: ' + self.target_instancename + '\n'\
                + 'Instance Id: ' + self.target_instanceid + '\n'\
                + 'Error: ' + error_message + '\n'
    
        # AWS/SNSの機能を使用し、障害メール通知を行う
        self.sns_action.send_message(topic_arn, subject, send_message)

By ta-boss

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です