Pull Request がMergeされました

はじめに

もうほんとにそのまんまのタイトルです。2回目のPull Request がMergeされて嬉しかったので記事にしました。

きっかけ

職場で、パラメータシートをなるべくリアルタイムで更新できるようにしていきたいなぁと思っていて、でもそれがなかなか実現できずにいました。そこで、なにか良いツールはないかなぁと思って、探していたらTextFSM とntc_templatesを使うと、いろいろできそうだな、と思って試してみました。

・TextFSM
 https://github.com/google/textfsm

・ntc_templates
 https://github.com/networktocode/ntc-templates

ある程度試してみて、とりあえずCisco ASAはこれで対応できるかな、と思って実機のConfigを出力して試したところ、エラーが出力されました。

どうする?

エラーを解読して、対処方法はわかったのでそれで良いかーと思っていたのですが、仕事で使うのならバージョンアップごとになにか手を加えるというのは避けたいなーと思い、思い切ってPull Requestを投げてみることにしました。(これが人生2回目です。)

しばらく音沙汰なかったので、こりゃダメかなぁと思っていたら、先日Mergeされましたの連絡が来て、本当に嬉しかったです。以下が該当のPull Requestです。

https://github.com/networktocode/ntc-templates/pull/783#event-3835534086

おわりに

せっかくMergeしてもらえたので、仕事でもntc_templatesを取り入れていきたいなぁと思っています。

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.  */
  }

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

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://gihyo.jp/dp/ebook/2011/978-4-7741-4890-8

内容と感想

数年前に読んだことがあったのですが、そのときは「ふーん」くらいの感想で特に印象に残ってはいませんでした。

低レイヤを知りたい人のためのCコンパイラ作成入門 を読んで実際に関数を実装したところくらいでもうちょっとアセンブラを理解したいなと思って、再度読みました。

以前よりもアセンブラに慣れてきたからか、思ったよりさらっと読めたのと、内容も理解できた気がします。

アドレッシングや関数の呼び出しや引数の取り扱いは低レイヤを知りたい人のためのCコンパイラ作成入門 でやっている内容とほぼ同じ(ただし、低レイヤ〜がAMD64で、アセンブラで〜がx86なので、引数の渡し方が違ったりはしています。)だったので、本当にスッと理解できた気がします。
気だけかもしれません。

共有ライブラリのところが、低レイヤ本の方ではやっていないテーマのようで、共有するための工夫というか苦労というかあるのだな、と分かって面白かったです。

最後の「継続は続く」はもともと、継続をよく理解できていないので、やっぱりあまり理解できませんでした。

今度はSchemeの勉強かな。

ちなみに

次は、[試して理解] Linuxのしくみ を読む予定です。

家にいる時間が増えてきたので、しっかりと力をつけたいと思います!

疲労困憊ら〜

はじめに

昨日はボルダリングに行きました。がんばった分、疲労もそれなりに。子どもたち(小学生)はお出かけもあまりできないし、スイミングスクールは休講だし、少しでも運動させてやりたいなぁと思ってがんばります。

さて、コンパイラ自作にチャレンジ中です、という記事です。

なんで自作?

以下の記事を見つけたのがきっかけでした。

・低レイヤを知りたい人のためのコンパイラ作成入門
 https://www.sigbus.info/compilerbook

もともと、コンパイラとかOSとかに興味があったので、やってみようかと軽い気持ちで始めたのですが、これがなんとも面白いのです。

何が面白いかというと

この本は執筆中なこともあり、途中から自分で内容を理解して、実装を進めないといけないようになっています。
もしかしたら、それが著者の狙いなんじゃないかと思うくらい、ちょうどいい感じなのです。

それなりに難しく、いろいろと試行錯誤するのですが、それによって今までなんとなくだった理解が、しっかりとした理解になっていくような気がします。

気がしているだけかもしれません。

まだ途中です

今は、関数を定義してフィボナッチ数列や階乗の計算ができるようになりました。なんとか最後(つまりC言語そのものをコンパイルできる)までがんばりたいと思います。

https://github.com/HideoYukutake/qcc

入門gitを読みました

はじめに

読書通帳にするか迷うところですが、技術系の本は別にしとこうと思います。

入門git
https://www.ohmsha.co.jp/book/9784274067679/

感想

刊行年が2009年なので、内容は少し古いです。
ただ、Git自体がそんなに劇的に変わっているわけではないので、読むのに支障はないと思います。コマンドの出力が少し異なるくらいでした。

Gitは普段からそれなりに使っているので、基本のコマンドは理解しているつもりでした。そのため、それほど新しい知見を得られたわけではないですが、たまにしか使わないコマンドとかについて振り返る機会になりました。

また、チームで開発というものをやったことがないので、ブランチの細かい使い分けとかが未だによくわかりません。本の中でもそのへんは科学じゃなくて技芸だよ、と書いてあるのである程度の経験を踏まえて、考えていくものなんだろうな、と思いました。

はじめてGitを使う人が読むと思いました。また、Pro Gitも合わせて読むとより良いな、と思いました。

Pro Git
https://progit-ja.github.io/

ペグソリティアを解く

はじめに

夏休みの旅行中に、うちの子がペグソリティアをやっているのを見て、 「これ解けたらドヤ顔できるな」と思ってチャレンジしたものの、意外に大変でした、というお話です。

解いてみる

チャレンジしたのは33ペグのやつです。
https://ja.wikipedia.org/wiki/%E3%83%9A%E3%82%B0%E3%83%BB%E3%82%BD%E3%83%AA%E3%83%86%E3%83%BC%E3%83%AB

いろんなパターンがあるようですが、一番スタンダードっぽいやつということで。

最初は、何回かチャレンジしてれば解けるやろ、と思っていたが全然できないです。 マジですか??ヤバイという気持ちになり、PCに解いてもらおうと考えたわけです。

プログラミングする

しかし、これも意外に難しい。というか全然意外ではなく、難しいことが知られていたらしいです。 私が知らないだけでした。

こういうのとか(英語なので半分も読めてないですけど)、こういうのとか見ると、現実的な時間で解の全部を解くことはかなり困難そうだ、という印象を受けました。

がんばりました

とはいえ、仕事のあとでちょびちょびやるだけだとそこまでのエネルギーは ないというわけで、とりあえず、簡単な感じで書いてみました。

https://github.com/HideoYukutake/puzzle

なんと!時間はものすごーくかかったものの、一応、解の一つが得られました。 夜寝る前に走らせて、翌朝には解けていたので、とりあえず良しとします。
(時間を計測するのを仕込むのを忘れてしまったのが残念。)

このあとは、性能を改善するのみ!現実的な時間でちゃんと解けるようになりたいなぁ。

2019/9/22追記

あのあとも少し工夫を続けた結果、解の一つであれば、数分で得られるようになりました。7時間くらい雨後し続けると50,000超の解を得ることができましたが、まだまだ探索空間が広すぎて全体を把握できていないようです。

対称性や回転で同一となるペグの配置ができたら解決済みとする、とかの工夫が必要だなぁと思いつつ、まだ解決できていません。

Goに入門

gopherfiveyears
The Go gopher was designed by Renee French.

はじめに

pythonrubyで書き、Cを読むことが多かったのですが、Goで書かれているプロダクトが増えてきた気がするので、Goに入門しようと思います。 自分の担当がインフラ周りだからかもしれないです。

参考書

この本を買いました。

入門Goプログラミング(NathanYoungman RogerPeppé 吉川邦夫)|翔泳社の本

インストール

まずは、インストール。 Go Playground でもOKとは書いてありましたが、なんとなくインストールしたいので 公式のページの案内のとおりにインストールしました。

vim-go の準備

エディタはvimを使っているので、vim-goをインストールすることにしました。

GitHub – fatih/vim-go: Go development plugin for Vim

上記URLのとおりにインストールしたつもりでしたが、guruがなんとかというエラーが 出たので、とりあえず、guruなどもインストールしました。 (エラーのキャプチャとかを取っておけばよかったです。)

go get -u golang.org/x/lint/golint
go get -u golang.org/x/tools/cmd/goimports
go get golang.org/x/tools/cmd/godoc
go get -u golang.org/x/tools/cmd/gorename
go get golang.org/x/tools/cmd/guru
go get -u github.com/nsf/gocode
go get -u github.com/rogpeppe/godef

再度、vimを開い :PlugInstall で無事にインストールできたみたいでした。

これから

参考書の各章の練習問題を解いていこうと思います。