IBM Cloud Classic 環境のサーバー情報一覧(詳細情報を含む)を CLI で取得し csv にする

ある日の事です

ボス：うちのアカウントの IBM Cloud Classic 環境のサーバー一覧が欲しいな
私：はい、CLI で関連に取れますよ
ボス：じゃあ、お願い!

うん、これでいいはず

VSI 用
``` 
ibmcloud sl vs list
```

表示項目は
```
id hostname domain cpu memory public_ip private_ip datacenter action
```

BMS 用
```
ibmcloud sl hardware list
```

表示項目は
```
id hostname domain public_ip private_ip datacenter status
```

私：出力しました
ボス：うーん、どんな OS で作成者が誰も知りたいな。Excel にしてくれるとうれしいな。
私：は～い('◇')ゞ

それで、この投稿になります。
せっかく、スクリプト化したので、自分のメモもかねて公開します。

なお、稼働確認は Window 11 の wsl ubuntu で行っています。

# 詳細の取得

上記のやり取りのように「list」で取得できる情報にはOSや作成者情報は含まれていません。
先ほどの「list」コマンドで「id」が分かるので「detail [ID]」を、各々に実行すれば詳細がわかります。

VSI 用
``` 
ibmcloud sl vs detail [ID]
```

BMS 用
```
ibmcloud sl hardware detail [ID]
```

あと、JSON で出力すると、つけていない場合の単純な表形式の出力では表示されななかった情報も得られます。

```
--output JSON
```

json 形式の出力なので jq と組み合わせて、json の path でピンポイントで欲しい情報が得られます。

# id 一覧の取得と利用

こんな感じで、JSON出力から、id のリストを取り出してループを回します。

```
ids=(`ibmcloud sl vs list --output JSON |  jq ".[].id"`)
# id 毎に明細データ処理
for id in "${ids[@]}"; do
# ここで、各サーバーを処理
done
```

# detail の出力は、標準? JSON?

素直に考えれば、JSON です。変なことをしなくても jq のパス指定で必要な値を直接取得できます。
ただ、JSON で取得できる内容を見たら細かすぎてパスを確定させるのが面倒!、今回必要なのは標準の表形式出力にあるので、そこから grep で抜き出せば、その方が楽かなと思ってしまいました。
そして テキスト検索・成型にはまりました。bash のループ、テキスト検索と成型のやり方について、すっかり忘れていました。
でも、今回は、そのまま突き進みました。

なお、VSI の場合の標準の表形出力は下記になります。

```
user01@6onoda:~$ ibmcloud sl vs detail 123456789
Name               Value
ID                 123456789
guid               XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX
Hostname           onoda-tgt
domain             onoda.local
fqdn               onoda-tgt.onoda.local
status             Active
state              Running
datacenter         tok05
os                 EL
os version         7.0-64 Minimal for VSI
cpu cores          2
memory             4096
drives             type     name   drive   capacity
                   System   Disk   0       25 GB
                   Swap     Disk   1       2 GB
                   System   Disk   2       10 GB
                   System   Disk   7       64 MB

public ip          165.xxx.xxx.xxx
private ip         10.xxx.xxx.xxx
private network    false
private cpu        false
transient          false
created            2022-10-21T00:50:59Z
updated            2023-02-21T07:18:05Z
last transaction   Cloud Instance Port Control (2023-02-21T07:18:05Z)
billing            Hourly
preset             B1_2X4X25
owner              xxxxxxx_6onoda@example.com
notes              -
tags               -
vlans              type      number   id
                   PRIVATE   858      3047062
                   PUBLIC    852      3047060

security groups    interface   id        name
                   public      3879902   onoda-tgt
```

# 今回取得したいこと

上記の出力で、下記から始まる行を grep すれは、今回の目的は達成できます。
「os」の後ろにブランクが二つ付いているのは「os version」と区別するためです。

```
"Hostname"
"public ip"
"private ip"
"os  "
"os version"
"datacenter"
"owner"
```

# VSI 用のcsv 出力用スクリプト

完成した VSI 用のスクリプトは下記になります。
items の配列は、出力したい列順に記載してください。上記でも書きましたが、「os」は grep でユニークになる程度にブランクを足す必要がありました。


```
#!/bin/bash
IFS=$'\n'

# 取得したい項目ヘッダーの配列
# 出力したい列順
items=(
  "Hostname"
  "public ip"
  "private ip"
  "os  "
  "os version"
  "datacenter"
  "owner"
)

# 項目の数
item_num=${#items[*]}

# id 一覧の取得
ids=(`ibmcloud sl vs list --output JSON |  jq ".[].id"`)

# id 毎に明細データ処理
for id in "${ids[@]}"; do

#  echo ${id}
  detail=(`ibmcloud sl vs detail ${id}`)
#   id を保持
    result[0]=${id}

#   明細各行毎に項目ヘッダーと突き合わせ
    for dline in ${detail[@]} ; do
      for (( i=0; i < ${item_num} ; i++)); do
        if (echo ${dline} | grep "^${items[i]}" > /dev/null) ; then
          temp=`echo ${dline} | grep "^${items[i]}"`
          result[i+1]=`echo ${temp} | sed -e "s/^${items[i]}//" | sed 's/^ *\| *$//'`
        fi
      done
    done

# id毎にデータをcvsで出力
  for (( i=0; i < ${item_num} ; i++)); do
    echo -n '"'${result[i]}'",'
  done
  echo '"'${result[i]}'"'
done
```

# BMS 用のcsv 出力用スクリプト
BMS 用のcsv 出力用スクリプトは下記になります。

```
#!/bin/bash
IFS=$'\n'

# 取得したい項目ヘッダーの配列
# 出力したい列順
items=(
  "Hostname"
  "public ip"
  "private ip"
  "os  "
  "os version"
  "datacenter"
  "owner"
)

# 項目の数
item_num=${#items[*]}

# id 一覧の取得
ids=(`ibmcloud sl hardware list --output JSON |  jq ".[].id"`)

# id 毎に明細データ処理
for id in "${ids[@]}"; do

#  echo ${id}
  detail=(`ibmcloud sl hardware detail ${id}`)
#   id を保持
    result[0]=${id}

#   明細各行毎に項目ヘッダーと突き合わせ
    for dline in ${detail[@]} ; do
      for (( i=0; i < ${item_num} ; i++)); do
        if (echo ${dline} | grep "^${items[i]}" > /dev/null) ; then
          temp=`echo ${dline} | grep "^${items[i]}"`
          result[i+1]=`echo ${temp} | sed -e "s/^${items[i]}//" | sed 's/^ *\| *$//'`
        fi
      done
    done

# id毎にデータをcvsで出力
  for (( i=0; i < ${item_num} ; i++)); do
    echo -n '"'${result[i]}'",'
  done
  echo '"'${result[i]}'"'
done
```

VSI と BMS 用が違うのは、対象が「vs」か「hardware」かという点だけです。

```
ids=(`ibmcloud sl vs list --output JSON |  jq ".[].id"`)
...
  detail=(`ibmcloud sl vs detail ${id}`)
...
```

```
ids=(`ibmcloud sl hardware list --output JSON |  jq ".[].id"`)
...
  detail=(`ibmcloud sl hardware detail ${id}`)
...
```


# 今回のやり方で出来ないこと
行のへッダーがユニークであることを前提にしているので、下記の System drive の情報はうまく取得できません。

```
drives             type     name   drive   capacity
                   System   Disk   0       25 GB
                   Swap     Disk   1       2 GB
                   System   Disk   2       10 GB
                   System   Disk   7       64 MB
```

また、当然ですが標準の表形出力に表示されない情報は取得できません。