Muttとgetmailを導入した話

はじめに

もともとメインのPCとしてThinkPad x61sを使ってきたのですが、さすがにスペックが弱いからか最近、動きがもっさりとしてきました。(OSはDebian10)

x220の方にLinux Mintを入れていたので、そちらを使うことが増えてきたので、メールの移動も兼ねてMuttとgetmailを導入した、という話です。

getmail

・getmail
http://pyropus.ca/software/getmail/

aptでぱぱっとインストールして、前のPCからgetmailrcをもってきて、とやったらGmailの認証エラーがでました。うーん?よく把握していないのですが、どうもOauth2を使った認証の方が良さそうということで、認証をOauth2に切り替えました。

仕組みをよく理解できていないので、とりあえず下記URLの記事を参考にgmail側の手順を済ませました。後日、ちゃんと勉強しないと、と思います。

・NeomuttでGmailにOAuth2.0する
https://qiita.com/ksuzuki_TVDn1/items/9b2f8d1f24ed81456793

getmailrc側の設定はこの記事のとおりで大丈夫でした。

・getmail-5.6 XOAUTH2 configuration for gmail
https://www.bytereef.org/howto/oauth2/getmail.html

メール落としてくるのに時間かかりました…

Mutt

Muttもaptでインストールして、元のPCのmuttrcを移動して完了です。

GPGで署名したメールと暗号化したメールがちゃんと検証・復号できているのか確認する作業が残っていますが、とりあえずは完了です。

2020/08/28 追記

署名と暗号化は問題なかったのですが、Gmail宛のSMTPで苦労しました。
まず、Gmail宛のSMTP設定ファイルを作成して読み込むようにします。

~.mutt/gmail.rc というファイルを作ります。

# Gmail SMTP Setting                                                                                                                        
 
set smtp_url = "smtp://#{自分のメールアドレス}@smtp.gmail.com:587/"
set smtp_authenticators = "oauthbearer"
set smtp_oauth_refresh_command = "/path/to/oauth2.py --quiet   
    --user=#{自分のメールアドレス}\
    --client_id=#{client_id}\
    --client_secret=#{client_secret}\
    --refresh_token=#{refresh_token}

それを読み込むようにmuttrcに設定します。

source "~/.mutt/gmail.rc"

とりあえず、これでOKです。

GPGで保護する

せっかくなので、認証周りの情報が書いてあるファイルはGPGで保護しようと思います。

gpg -r #{自分のメールアドレス}.com -ea gmail.rc

muttrc の記載を修正。ちゃんと送信できることを確認して、元のテキストファイルはバックアップメディアに移して削除しました。

source "gpg -d ~/.mutt/gmail.rc.asc |"

おうち林間学校最終日

はじめに

楽しかったおうち林間学校ももうすぐ終わりです。

1日目
2日目

まずはチェックアウトの準備

朝食を食べたら、ベッドのシーツを外してたたんだり、ゴミを分別したり、チェックアウトの準備です。林間学校なので、子どもたちにもやってもらいます。ちゃんとできてえらかったね。

もう少し那須で遊ぼう

せっかくなので、ということで、お菓子の城に行きました。

・お菓子の城
 https://www.okashinoshiro.co.jp/

花と体験の森の中で、小5ちゃんはハーバリウム作り、小2ちゃんはデコスイーツ作りを体験です。真剣な眼差しで取り組んで、素敵な作品を作りました。

ハーバリウムを作っている様子
デコスイーツを作っている様子

この後、お土産も買って、楽しかった那須とはお別れです。

餃子を食べよう

みんな餃子が大好きなので、宇都宮の餃子を食べようということになり、宇都宮みんみんさんの鹿沼店に行きました。(高速のインター降りて割とすぐなので)
・宇都宮みんみん
 http://www.minmin.co.jp/

15分ほど並んで、いざ店内へ。焼き餃子4人前に水餃子・揚餃子を1人前ずつとライスを頼んでいただきました。おいしくいただきました。

家に着くまでが林間学校

ということで、安全運転を心がけ、15時くらいに無事に自宅に到着しました。
ちょっと疲れたけど、パパママにとっても良い思い出になる2泊3日でした。

娘たちへ。ありがとう!

所感

今回、Covid-19の感染が全国で広がる中、那須へおうち林間学校へ行くことに少しためらいもありました。コテージ泊なので、それほど多くの人と接するわけではありませんが、誰とも接しないわけではありません。手洗い・マスクをしっかり対策して、感染しない・感染させないように気をつけての旅行でした。感染に注意しながらでも、娘たちはとても楽しんでくれたので、行ってよかったな、と思いました。

一方で、平日を使った日程だったこともあり、道路も那須周辺もそれほどの混雑というわけではありませんでした。夏休みシーズンであることを考えると、もっとたくさんの人が来ていても良いはずの場所なので、やはりCovid-19の影響はあるのかなと感じます。

とても素敵な3日間をくれた栃木県に、また以前のような賑わいが戻るといいなと思いました。

おうち林間学校2日目

はじめに

おうち林間学校の2日目です。1日目はこちら

那須サファリパークへ

最初に向かったのは那須サファリパーク。ヒコヒココテージからは車で5分程度の近距離です。9時のオープン直後に入ったので、すいていましたね。

餌を買って、いざ出発!ところが、思いのほか動物たちがグイグイ来るので娘たちはものすごいビビリよう。大人でもちょっと焦るレベルでした。とくにシマウマなどのエリアでは、車の前にどーんと構えて餌くれないと通さないぞという固い決意を受けて、進むこともできず。ものすごくゆーっくりと車を前進させることでなんとか道を開けてもらって、通過できました。畳んでいたサイドミラーもお馬さんのせいで、完全に開いているし…。大人もビビるなか、頑張って餌やりをした娘たちでした。

サファリコース自体は30分ほどで終了したのですが、その後の、ふれあいコーナーでうさぎやテンジクネズミ(モルモット)に餌を上げたり、トラやライオンを遠目に見たりして楽しみました。

キリンやゾウに餌やりできるコーナーもあり、そこでも娘たちは動物たちに餌やりを熱心にやっていました。このときにはだいぶ慣れていたのだと思いますが、あまり怖がってなかったです。慣れが大事なんですね。ヤギの散歩をしていたスタッフさんがいたので、質問していたら、違う親子連れがきて「うちもヤギ飼ってるんですよー。でも触らせてくれなくて。」とスタッフさんに。うちの子が「ヤギ飼いたい」とか言い出したらどうしようかしらん。

ちょうどよい時間になったので、サファリパーク内のレストランで食事をしました。おいしかったです。

吊橋ツアー

午後は、つつじ吊橋へ向かいました。
・つつじ吊橋
 https://www.nasukogen.org/charm/

駐車場から、遊歩道を数分歩くと吊橋に到着です。立派な吊橋なのですが、高さがかなりあるのと、下を見ることができようになっている箇所があるのとで、結構怖いです。途中、風が吹いたときはそれなりに揺れて怖かったですが、子どもたちは平気なようで、はしゃいでいました。
※パパは高所恐怖症です。

その後、殺生石まではあるいで650mとあったので、歩いて行くことにしたら、これが見事に山道を歩くことになりました。アップダウンもあり、往復するともう汗びっしょりでした。殺生石自体は子どもたちも特段の感動はなかったようですが、山道は記憶に残ったようでした。

時間に余裕があったので、もう一つの弁天吊橋に向かうことにしました。
こちらは、つつじ吊橋よりもさらに奥に入る場所で、駐車場付近もかなり寂れていて、ちょっと不安になりながらの訪問でした。駐車場からGoogleMapに従って進むものの、目的地付近にはつけず、途中にあった看板付近から山道に入ることで吊橋に行くことができました。

これで本当に着くのかなと不安になるような道です。こんな感じの道を進みます。

なんとか無事に到着です。

つつじ吊橋よりも小ぶりですが、風情というかスリルはこっちのほうがあります。数人で渡ると結構ゆれるので、パパは恐る恐るでした。相変わらず、娘たちは余裕でしたね。

少しゆっくりと

予定外の山道歩きで疲れたので、那須の中心地?に戻って、少しゆっくりしました。フィンランドの森の中のハンモックカフェでお茶をして、コテージに戻って夕飯の準備です。

・フィンランドの森
 https://www.finlandnomori.net/

夕飯そして2日目終わり

夕飯の準備も自分たちで。買い出しチームと火起こしチームに分かれて、準備です。ママと小5ちゃんはお買い物に。メインの食材は「とちぎ和牛」です。パパと小2ちゃんは火起こし。七輪に炭を入れて、着火剤を使って火起こし。無事に準備できました。

霜降りの舌の上でとろけるような感じではなくて、赤身のしっかりとした肉の旨味を感じるお肉でした。赤身ですが、程よい柔らかさで娘たちも大満足のBBQでした。

その後片付けをしてお風呂に入ってベッドへ。疲れたのか、娘たちも早々に眠りに落ちたところで、無事に2日目も終わりました。

おうち林間学校1日目

はじめに

Covid-19の影響で娘(小5)の通う小学校の林間学校が中止になってしまったので、家族で林間学校しよう!ということで那須に来ています、の記録です。

もともとは

毎年群馬県のどこかに行っていたらしいのですが、今年から長野県のどこかに変わりますというお知らせを受けていました。詳細は決まり次第だったのが、中止の方が先に決まったので、結局どこだったのかわからず。

長野・群馬は以前も行ったことがあったので、学校とは違うけど栃木の那須にしようということにしました。

出発

9:00過ぎに車に荷物を詰め込んで出発。早々に娘(小2)がお人形を忘れたというので引き返すなどしてなんやかんやで11時過ぎに佐野SAに到着。

小5ちゃんが学校の授業で全国のうどんのことを調べていたので、「みみうどん」を食べるのが寄った目的です。

娘の感想は「うどんじゃないよね」でした。でもおいしそうに全部食べました。

パパは佐野ラーメン。こちらもおいしかったですよ。

いわむらかずお絵本の丘美術館へ

小2ちゃんが「14ひきシリーズ」大好きなので、絵本の丘美術館へ立ち寄り。

いわむらかずお絵本の丘美術館
http://ehonnooka.com/

途中、だいぶ細い道を通りながらも無事到着。小高い丘の上にあって、展望ひろばからの見晴らしは素晴らしかったです。トノサマガエル(だと思います)を見つけたり、バッタをつかまえようとしたり、素朴な遊びができる素敵な場所でした。もうちょっと遊びたかったのですが、小5ちゃんのアレルギー(なんかの花粉でしょうか)がひどくなったので、少し早めに切り上げることにしました。

キャンプ場に到着

今回止まったのはヒコヒココテージ那須です。

ヒコヒココテージ那須
http://hikohiko.com/nasu/

林間学校なのでテントじゃなくて、コテージにしました。晩御飯は子ども中心でカレーを作ってくれました。(といいつつ、だいぶマミー子さんがやってくれましたけど…)

ヒコヒココテージは設備が充実しているので、林間学校っぽさはないですが、デッキでご飯食べたり、花火をしたりと楽しむことができました。

2日目へ続きます。

ファイルシステムが壊れたらDHCPクライアントも暴走した話

はじめに

職場で一部の人から「ネットワークにつながらない」という申告があり、調べたら自分が原因でした、というお話です。

朝のことです

一部の職員から「ネットワークにつながりません」という申告がありました。朝で担当の職員がいなかったこともあり、自分が代わって対応しました。

確認できたのは以下のような事象でした。

  • 会社の無線LANのうち、あるSSIDではつながらない
  • 別のSSIDなら大丈夫
  • ipconfigしてもらうと、つながらないというSSIDはIPを取得できていない
  • Wifiはつかめているっぽい

PCのIPアドレスはDHCPなので、DHCPサーバになにか問題が起きたかなぁというのが私の見立て。

しかし、この時点でいくつか疑問が。

  • DHCPサーバの不具合なら、別のSSID(別セグメント)では問題起きないのは不思議
  • DHCPリレーの不具合でも同様
  • 問題のあるSSIDも、大丈夫な人と大丈夫じゃない人がいる。DHCPサーバの問題なら大丈夫な人がいるのも不思議。
  • 大丈夫じゃない人も複数いる。PC再起動してもダメ。PC起因が複数台で同時発生というのもありえなくないけど、確率低い

そんなこんな考えると、IPアドレス枯渇が一番ありえそうな気がする一方で、つながらない人が全員、後から接続しようとしたわけじゃなく、大丈夫な人よりも先につなごうとしていたので、はっきりと断言できるわけでもなし。

調査結果を聞く

ネットワークの担当者が来たので、そちらに任せていると「DHCPのアドレス枯渇が原因です。特定のクライアントから10秒毎に新しいIPを払い出すよう依頼が来ている」という連絡が。しかも、私が実験用に使っているPCから。

そのPCはMinIO の実験用に職員用セグメントに立てていたDebianマシン。そういえば、昨日の午後にファイルの送受信試したな、と。
マシンを見てみると、IOエラーが大量に出ているようですが、sshも通るしネットワーク的にはおかしくはない感じ。IPアドレス確認しようとコマンド打ったらびっくり!確かに有線のinterfaceにIPアドレスが大量に割り当ててられているではないですかー。とりあえず迷惑かけ続けるのもアレなので、シャットダウンしました。

問題のマシンを調査してみる

IOエラーがDHCPの暴走になるのか?というのが疑問で、所定の用事を済ませたた後に、問題のマシンを調査してみました。

問題起こすわけにもいかないので、いったん、有線ケーブルは抜いて起動して、syslogとかを見てみようとすると、ガーン!起動しないじゃないの。手動でfsckしてね、と言われるので、言われるがまま作業してなんとか起動。

で、syslog見てみると、どうも前日の18時前くらいからログがない様子。その少し前のログを見るとIOエラーでファイルシステムをReadOnlyに、と書かれているので、syslogも書き込めなくなったのかも、という結論に。

しかし、それでなぜDHCPが?syslogを見ると前日の12時くらいにリース更新の記録があり、残りが12時間弱くらいになっていたのです。そういえば、前日の23時台から問題の挙動が起き始めている、とネットワーク担当者が言っていたな…

dhclient-enp2s0.leasesを見ると、正常に取得していたIPだけが書かれていて、それがsyslogの内容とも概ね合致している感じ。その後IPを大量に取得しているにもかかわらずそれがleasesに反映されていない。

ということは、こんな感じってことですかね。

DHCPのリース更新をした
→ちゃんと更新できているにもかかわらず、DBを更新できないためにリース期限を更新できなかったと思いこむ
→リース切れだと思い、IP取得に行く
→取得できているのにDB更新できないから取得できなかったと思い込み、また取得に行く(以下これを繰り返す)

まとめ

問題のマシンは、以前もファイルシステムエラーを起こしていたので、ディスクが壊れているのか、MinIOとext4の組み合わせが良くないのか、ちゃんと調べてから使うべきでした。

きちんと調査せずに、安易にOS再インストールして、かつサーバのように起動させっぱなしにしていたのが良くなかったです。

psコマンドとかで見た限り怪しいプロセスもなかったし、おそらくセキュリティインシデント(マルウェア感染とか)ではなかったと思われるので、それは良かったです。

参考にしたサイトなど

あと、dhclientのソースコードもちょっとだけ目を通しました。

Pull Requestしてみた

はじめに

4連休を使って、ntc-templatesというOSSへPull Requestしてみたメモです。

きっかけ

職場でFWポリシーを常に最新に保ちたいというニーズがあります。でも、手作業でエクセルのポリシー表をぽちぽち修正するのってツライし、ミスる原因にもなります。そのため、FWにACLを投入した内容が自動で反映できるようになるといいな、と思ったのがきっかけです。

いろいろ試行錯誤してみました。最初に試したのはこれでした。

自由度が高くて使いやすいのですが、投入したConfigの中身を自分で解釈していく必要があってやめました。

なにか良いツールないかなと思って見つけたのがTextFSMでした。このあたりの記事などを参考にしました。

使ってみたところ

TextFSMだけだとテンプレートを自分で書かないといけないようですが、そこはやっぱり先人がいて、ntc-templatesというOSSがありました。

そこで使ってみると、これは便利だ!ということで早速、職場のASAから出力したACLでもやってみることにしました。

ところが、”Did not match any rules” エラーが出てしまいます。微妙にテンプレートの構文とコマンドの出力が合っていないようでした。

じゃあ自分で修正してみよう

自分で修正したら良いね、ということでCiscoのコマンドリファレンスなどとにらめっこして、修正しました。修正したのは主に3点。

  • ENTRY_PORTが”ftp-data”のようなハイフン付きにも対応できるようにした
  • inactive (hitcnt=0) (inactive) のような出力に対応できるようにした
  • log disableやlog default に対応できるようにした

Pull Requestの内容がこれです。
https://github.com/networktocode/ntc-templates/pull/783

英語でのやりとりになるので、そこにも注意しました。英語苦手なので。

おわりに

マージしてもらえたら嬉しいですし、されなくても指摘とか貰えればそれはそれでありがたいです。結構手間暇かかって大変でしたけど、おもしろかったです。

df コマンドを調べてみた

はじめに

職場で、mount で表示すると出てくるのに、dfだと表示されないものがあるというので、ちょっと勉強してみたのでそのメモです。

手がかり

職場での出来事なので、あまり詳細について書くことができません。

NFSサーバの3つのディレクトリをそれぞれ、別のマウントポイントでマウントしているそうです。そのうちの1つだけが表示されない状況。
そうすると、NFSサーバ側の問題は考えにくいと思います。

複数のマシンからマウントしているみたいですが、特定の1つだけdfで表示できないっぽい感じ。(このあたり詳細を聞いていないのでなんとも。)

mountコマンドで表示すると出てくるのに、dfだと出てこない。
あと、マウントポイントの先のデータも参照等できている。(なのでマウント自体はできていると思われる。)

仮説: dfを疑おう

dfとmountでおそらく情報源となっているものが違っていて、それが何らかの原因で差異が生じているのでは、と仮説を立ててみた。

mount コマンドの情報源

マニュアル見ると書いてあった。

mount および umount プログラムによって現在マウントされているファイルシステムの一覧は /etc/mtab ファイル中に記述されている。 mount が
引き数なしで実行された場合には、 このリストが表示される。

man 8 mount より

自分の手元のPC(Linux Mint 19.3)だと /etc/mtab は/proc/self/mounts へのシンボリックリンクでした。職場のはRedHatEnterpriseLinuxの7系だった気がするので、また違うかもしれない。

df コマンドの情報源

df のマニュアル見たけど、特に書いていない。うーん、しょうがない。ソースコードを読もう。でもバージョンわかんない。まぁいいや。とりあえず割と新しいやつを読んでみておこう、というわけで GNU coreutilsの8.32を読むことに。

特に手がかりとかないので、まずはmainから。

int
main (int argc, char **argv)
{
  struct stat *stats IF_LINT ( = 0);
  initialize_main (&argc, &argv);
  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  atexit (close_stdout);
  fs_select_list = NULL;
  fs_exclude_list = NULL;
  show_all_fs = false;
  show_listed_fs = false;
  human_output_opts = -1;
  print_type = false;
  file_systems_processed = false;
  exit_status = EXIT_SUCCESS;
  print_grand_total = false;
  grand_fsu.fsu_blocksize = 1;
  /* If true, use the POSIX output format. */
  bool posix_format = false;
  const char *msg_mut_excl = _("options %s and %s are mutually exclusive");

  while (true)
    {
      int oi = -1;
      int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, &oi);

最初の方のやつは、GNUのコマンドでよく出てくるやつですね。(前にls を読んだときにもあった。)その後の変数は名前から推測すると、コマンドのオプション値を保存するためのものかなぁと思います。while文はコマンドオプションの処理だと思います。

というわけでこの辺をずーと飛ばしてみると、1780行目にマウントしているもののリストを取り出しているっぽいところを発見。

mount_list =
  read_file_system_list ((fs_select_list != NULL
                          || fs_exclude_list != NULL
                          || print_type
                          || field_data[FSTYPE_FIELD].used
                          || show_local_fs));

read_file_system_list は lib/mountlist.c にありました。

struct mount_entry *
read_file_system_list (bool need_fs_type)
{
  struct mount_entry *mount_list;
  (省略)
#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
                             also (obsolete) 4.3BSD, SunOS */
  {
    FILE *fp;
#if defined linux || defined ANDROID
   /* Try parsing mountinfo first, as that make device IDs available.   
      Note we could use libmount routines to simplify this parsing a 
      little (and that code is in previous versions of this function), 
      however libmount depends on libselinux which pulls in many 
      dependencies. */ 
    char const *mountinfo = "/proc/self/mountinfo";
    fp = fopen (mountinfo, "r");
    if (fp != NULL) 
      {
        (省略) 
      } else /* fallback to /proc/self/mounts (/etc/mtab). */
#endif /* linux || ANDROID */
      {
        struct mntent *mnt;
        char const *table = MOUNTED;
        fp = setmntent (table, "r");
         (省略)

どうやら、最初に/proc/self/mountinfo を読もうとしていて、それができないときに/etc/mtab にフォールダウンするみたいですね。

ちなみに、MOUNTEDは、マクロで_PATH_MOUNTEDと定義されていました。_PATH_MOUNTEDは、glibcのsysdeps/unix/sysv/linux/paths.hによると/etc/mtab なので、上記の記載とも一致しています。

結論: まだわからないです

出勤したら、また調べてみようと思います。
とりあえず、/proc/self/mountinfo と/etc/mtab の差異を調べて見る感じですね。でも、自分がログインして調べるわけじゃないのが、ちょっと面倒です。

サーバの運用保守を委託してやっているので、あんまり自分がログインして、とかすると責任分解が、とか言われてしまうのです。とほほ。

2020/07/06 追記

原因は、このChangeLogにある内容でした。

df: prioritize mounts nearer the device root
In the presence of bind mounts of a device, the 4th “mount root” field
from /proc/self/mountinfo is now considered, so as to prefer mount
points closer to the root of the device. Note on older systems with
an /etc/mtab file, the source device was listed as the originating
directory, and so this was not an issue.
Details at http://pad.lv/1432871

coreutils/ChangeLog

ソースコードの続きの箇所で、filter_mount_list() という関数があって、そこで、bind mount の場合に重複して表示させないように処理しつつ、NFSの場合は意図したものとして、ちゃんと表示するように書いてありました。

bool target_nearer_root = strlen (seen_dev->me->me_mountdir) 
                          > strlen (me->me_mountdir);
/* With bind mounts, prefer items nearer the root of the source */bool source_below_root = seen_dev->me->me_mntroot != NULL
                    && me->me_mntroot != NULL
                    && (strlen (seen_dev->me->me_mntroot)
                        < strlen (me->me_mntroot));
if (! print_grand_total
    && me->me_remote && seen_dev->me->me_remote
    && ! STREQ (seen_dev->me->me_devname, me->me_devname))
  {
    /* Don't discard remote entries with different locations,
       as these are more likely to be explicitly mounted.
       However avoid this when producing a total to give
       a more accurate value in that case.  */
  }

無駄といえば無駄でしたが、いろいろ勉強になったのでラッキーでした。

読書通帳⑨

読んだ本

・たのしいムーミン一家(講談社文庫)
 
https://bookclub.kodansha.co.jp/product?item=0000139838

内容と感想

以前に読んだムーミン谷の彗星の続きです。妻がだいぶ以前に購入していたものがあるというので借りて読みました。今は娘の本になっているみたいです。

ムーミン谷の彗星がどことなく暗い感じがある作品でしたが、今回は全体に明るくタイトル通りに「たのしい」ストーリーでした。

飛行おにの帽子(黒いシルクハット)を入手したことでいろんな不思議なことが起きて、ゆかいなキャラクターたちの不思議な世界観と合わさって、どんどん読み進めることができます。

最後に飛行おにがでてきて、すごい怖いキャラクタなのかと思っていたら、そんなことはなく、全体にハッピーな気分で読み終わることができました。ちなみにうちの娘は、ムーミンバレーパークの飛行おにのジップアドベンチャーが大好きです。ずっと「飛行おにってなんだろう」と思っていたので、それがわかってよかったです。
※私は高いところが苦手なので、ジップアドベンチャーはやりません。

本の中で、じゃこうねずみの入れ歯が怪物に変わってしまうシーンがあって、作者注として「なんに変わったか知りたかったら、お母さんに聞いてみて」と書いてあります。北欧にはなにかそういう言い伝えとかがあるのかな、と思って調べてみました。でも、これって家族でいろんな想像を膨らませてね、というメッセージだったみたいです。うーん、お父さんは想像力が欠けているなぁ。

まとめ

続きを読みたいので、続編を買って読もうかな。最近は電子書籍で読むことが多いです。

Amazon Connect でなんちゃって監視センター作ってみる

はじめに

システムを運用していると、どうしても夜間に電話での呼び出しが必要になる場面があります。そのためにオペレーションセンターを利用することもあると思いますが、電話かけるだけなら、なんとかできないかなと考えてみました。

まぁPagerDutyが良いと思いますが、AWS使っているんなら、Amazon Connectでも良いんじゃないかと思ってやってみた、というわけです。

処理フロー

なんらかの形でS3にファイルをアップロードする
→S3へのPUTを検知してLambdaを起動
→LambdaがAmazon Connectを呼び出して、電話をかける

S3にファイルをアップロードするスクリプト

from datetime import datetime
import boto3
AWS_S3_BUCKET_NAME = 'myalertcall'
PUT_OBJECT_KEY_NAME = datetime.now().strftime('%Y%m%d%H%M%S')

s3 = boto3.resource('s3')
bucket = s3.Bucket(AWS_S3_BUCKET_NAME)
obj = bucket.Object(PUT_OBJECT_KEY_NAME)
body = """ これはテストアラートです。
test03
"""
response = obj.put(
    Body = body.encode('utf-8'),
    ContentEncoding = 'utf-8',
    ContentType = 'text/plain'
)

Lambdaのスクリプト

from urllib.parse import unquote_plus
import boto3

def lambda_handler(event, context):
    alert_bucket_name = event['Records'][0]['s3']['bucket']['name']
    alert_object_key = unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    service_name = '適当なサービス'
    connect = boto3.client('connect')
    common_message = 'もうほんとやばいっち、いいよろうもん。なんとかしてっちゃ!'
    message = '{}が大変ばい。{}'.format(service_name,common_message) connect.start_outbound_voice_contact(
        DestinationPhoneNumber='+81xxxxxxxxxx',
        ContactFlowId='yyyyyyyyyyy',
        InstanceId='zzzzzzzzzzzzzz',
        SourcePhoneNumber='+81wwwwwwwwww',
        Attributes={ 'message': message }
    )

問い合わせフロー

参考にした記事と同じフローを作りました。

まとめ

Amazon Connectが何なのか全くわかっていない状態からでも、1時間もせずに電話発信ができるようになりました。Amazonすごい。

あとは、発信先が電話を取らない場合は次の発信先にかけるとかのルールを作れるようになろうと思います。もうちょっとAmazon Connectを勉強しないと。

現状のスクリプトでもS3からオブジェクト名を取れるので、それで処理を分けたり、ファイルの中身をみて処理を変えたりとかできるとより便利そうだな〜とか考えています。

参考にしたサイト

・Amazon Connectで月4ドルで電話発信する仕組みを構築する
 https://dev.classmethod.jp/articles/amazon-connect-system-alert/

2020/05/17 追記

このサイトを参考にして、電話に出ないと次の人にコールするように変えてみた。
・[Amazon Connect] 任意時間で電話の呼び出しコールを中断する方法を考えてみた
 https://dev.classmethod.jp/articles/amazon-connect-stop-calling/

本当はS3にアップしたファイルの中身とかでメッセージを変えたり、発信先を変えたりすることになるのかなと思います。

import time
from urllib.parse import unquote_plus
import boto3


def lambda_handler(event, context):
    contact_flow_id = 'hogehoge' # 自分の環境の値に変える
    instance_id = 'fugafuga'
    # alert_bucket_name = event['Records'][0]['s3']['bucket']['name']
    # alert_object_key = unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    connect = boto3.client('connect')
    common_message = 'とっても大変なことが起きました。もうほんとやばいです。なんとかしてください。'
    message = f'{common_message}'
    call_numbers=('phone01','phone02')  # 通知したい電話番号
    for number in call_numbers:
        res = connect.start_outbound_voice_contact(
                  DestinationPhoneNumber=number,
                  ContactFlowId=contact_flow_id,
                  InstanceId=instance_id,
                  SourcePhoneNumber='発信番号',
                  Attributes={ 
                      'message': message,
                      'is_recieved': 'calling',
                  }
        )
        contact_id = res['ContactId']
        time.sleep(10)     # 時間は適当に調整する
        attr = connect.get_contact_attributes(
                   InstanceId=instance_id,
                   InitialContactId=contact_id
                )
        if attr['Attributes']['is_recieved'] == 'recieved':
            break
        connect.stop_contact(
            InstanceId=instance_id,
            ContactId=contact_id
        )

読書通帳⑧

読んだ本

・新装版 ムーミン谷の彗星(講談社文庫)
 https://www.moomin.co.jp/books/2407

内容と感想

実は44才で初めてのムーミンです。もちろんムーミンというキャラクタは知っていましたが、ストーリーは全く知りませんでした。アニメも観たことがなかったのです。で、なんで急にどうしたの?と思うわけですよね。

今、小5になる娘がいまして、その子がムーミンを読んでハマっておりまして、話題についていけるようにと読みました。

ムーミン谷の彗星は、童話チックな内容を想像していたらちょっと違っていて、少し怖い感じもあるストーリーでした。ムーミン谷に彗星が迫ってきて、それを調べるために天文台に向かって…

スニフやスナフキンもすべてが初登場ということで、とても新鮮な感じです。スノークのおじょうさんとムーミンの出会いも素敵でした。

年甲斐もなくムーミン谷は最後、どうなってしまうのだろうとハラハラしました。とはいえ、決してハラハラドキドキの連続で手に汗を握る、というものではなく、ムーミンの不思議な世界と愉快なキャラクタが、なんだかふんわりした気持ちにさせてくれる本です。

1日半くらいで読み終わったら、娘が「楽しいムーミン一家」を貸してくれたので、今読んでいます。

ちなみに

少し前に飯能のムーミンバレーパークにも行ったのですが、今行ったらもっと楽しいかもという気になっています。残念ながら休園していますが…

・ムーミンバレーパーク・メッツァビレッジ
 https://metsa-hanno.com/