Rでdhcpd.leasesを読み込んでみた

昔rubyで作った dhcpd.leases を読み込むスクリプトを眺めていたところ「そういえばRでこの手のCSVとかじゃないファイルを処理したことないな」と意味もなく思ったので、じゃあ試しに作ってみよっかていう話です。

使用用途は全く考えていないです。dhcpサーバーにRがインストールされているなんていう場面は想像できないので、`scp`コマンドや`rsync`コマンドでローカルにして処理するって話になるかなと思うんですが、それでも何に使うんだ？ってな話です。

## dhcpd.leasesの中身と攻略方法
dhcpd.leasesはの中身は以下のようなフォーマットとなります。これをどうやって変換しようかという話なんですが、今回は「{}」で囲まれているからというか私が好きだからという理由でJSON形式に整形してパースすることとしました。JSON形式のパースはライブラリまかせなので dhcpd.leases をJSON形式に整形するスクリプトを書けば良いという話になります。

```text:dhcpd.leases
lease 192.168.0.126 {
  starts 5 2019/02/01 09:31:09;
  ends 6 2019/02/02 01:31:09;
  binding state active;
  next binding state free;
  hardware ethernet b8:08:cf:e4:11:11;
  uid "\001\270\010\317\344\021\021";
  client-hostname "foo";
}
lease 192.168.0.130 {
  starts 5 2019/02/01 05:40:01;
  ends 6 2019/02/02 03:40:01;
  binding state active;
  next binding state free;
  hardware ethernet 9c:e3:3f:b5:22:22;
  uid "\001\234\343?\265\042\042";
  client-hostname "bar";
}
```

## 作ってみたスクリプト
スクリプトの流れとしては攻略方法のとおりで、 dhcpd.leases を読み込んでJSON形式に整形して、それを `fromJSON()` でパースして data frame形式に変換するという流れです。
ある程度使ったこのある書き方で書きたいなと思ったので、data.tableとかとか、そのライブラリ使わなくてもできるんじゃない？っていうライブラリも私好みで使いまくっています。
それと「binding state」みたいにスペースが入っていたりすると扱いにくかったので「state」みたいに名前を変えていたり、ターゲットを「starts」「ends」「binding state」「hardware」「client-hostname」のみを絞っちゃったりしています。

```R:dhcpd_leases.R
library(dplyr)
library(stringr)
library(data.table)
library(lubridate)
library(jsonlite)

dhcpd_leases <- fread("dhcpd.leases", sep=NULL, header=FALSE, data.table=FALSE, col.names="t") %>%
    mutate(t=str_trim(t)) %>%
    mutate(t=str_replace(t,'^(starts|ends) [0-9] (.+);', '"\\1":"\\2"')) %>% #対象とする行の末尾は;を入れない
    mutate(t=str_replace(t,'^binding state (.+);', '"state":"\\1"')) %>%
    mutate(t=str_replace(t,'^hardware ethernet (.+);', '"ethernet":"\\1"')) %>%
    mutate(t=str_replace(t,'^client-hostname (.+);', '"hostname":\\1')) %>%
    mutate(t=str_replace(t,'^lease ([0-9.]+) \\{', '{"ip":"\\1"')) %>%
    filter(!str_detect(t,";$")) %>% #今回対象としない行（末尾が;）は削除
    filter(!str_detect(t,"^#")) %>%
    filter(!str_detect(t,"^$")) %>%
    `[[`("t") %>%
    str_flatten(",") %>%
    str_replace_all(",\\}", '}') %>%
    str_replace("\\},$", '}') %>%
    sprintf("[%s]", .) %>%
    fromJSON() %>%
    mutate(starts=parse_date_time(starts,"%y/%m/%d %H:%M:%S",tz="GMT")) %>%
    mutate(ends=parse_date_time(ends,"%y/%m/%d %H:%M:%S",tz="GMT")) %>%
    mutate(starts=with_tz(starts,"Asia/Tokyo")) %>% #タイムゾーンを日本に変更
    mutate(ends=with_tz(ends,"Asia/Tokyo"))
```

## 実行結果
変換後は以下のような感じとなります。

```text:実行結果
> # テスト（内容確認）
> options(width=120)
> dhcpd_leases %>% head
             ip              starts                ends  state          ethernet hostname
1 192.168.0.126 2019-02-01 18:31:09 2019-02-02 10:31:09 active b8:08:cf:e4:11:11      foo
2 192.168.0.130 2019-02-01 14:40:01 2019-02-02 12:40:01 active 9c:e3:3f:b5:22:22      bar
3 192.168.0.250 2018-05-24 11:36:50 2018-05-25 03:36:50   free 4c:57:ca:18:cc:cc     <NA>
4 192.168.0.146 2019-02-01 18:16:56 2019-02-02 10:16:56 active 00:1e:33:28:aa:aa      baz
5 192.168.0.145 2019-02-01 18:19:34 2019-02-02 10:19:34 active 9c:b7:0d:72:dd:dd      qux
```

元のデータは以下のような感じ

```text:dhcpd.leases
lease 192.168.0.126 {
  starts 5 2019/02/01 09:31:09;
  ends 6 2019/02/02 01:31:09;
  binding state active;
  next binding state free;
  hardware ethernet b8:08:cf:e4:11:11;
  uid "\001\270\010\317\344\021\021";
  client-hostname "foo";
}
lease 192.168.0.130 {
  starts 5 2019/02/01 05:40:01;
  ends 6 2019/02/02 03:40:01;
  binding state active;
  next binding state free;
  hardware ethernet 9c:e3:3f:b5:22:22;
  uid "\001\234\343?\265\042\042";
  client-hostname "bar";
}
lease 192.168.0.250 {
  starts 4 2018/05/24 02:36:50;
  ends 4 2018/05/24 18:36:50;
  tstp 4 2018/05/24 18:36:50;
  binding state free;
  hardware ethernet 4c:57:ca:18:cc:cc;
  uid "\001LW\312\030\314\314";
}
lease 192.168.0.146 {
  starts 5 2019/02/01 09:16:56;
  ends 6 2019/02/02 01:16:56;
  binding state active;
  next binding state free;
  hardware ethernet 00:1e:33:28:aa:aa;
  uid "\001\000\0363(\252\252";
  client-hostname "baz";
}
lease 192.168.0.145 {
  starts 5 2019/02/01 09:19:34;
  ends 6 2019/02/02 01:19:34;
  binding state active;
  next binding state free;
  hardware ethernet 9c:b7:0d:72:dd:dd;
  uid "\001\234\267\015r\335\335";
  client-hostname "qux";
}
```

## 思ったよりもするするっと書けた
最初はどうかなーって思っていたんですが思ってた以上に気持よく書くことができました。あと試しに10万行くらいのデータを作って読み込ませてみましたがストレスなく動きましたので速度的にも問題なさそうです。

R使う機会増やしてみようかなって思う今日このごろです。