LoginSignup
5

More than 3 years have passed since last update.

posted at

updated at

Oracle Cloud(OCI)のcliのinput/outputで使うjsonに慣れる

2019/01/30 新規作成
2019/02/04 TIPS追加


Oracle Cloud Infrastructure(OCI)のcliに限らず、広く使われているjsonですが、
やや取り扱いに戸惑うことがあるのでまとめてみました。

1.json基礎

jsonとは?

JSON (JavaScript Object Notation)は、軽量のデータ交換フォーマットです。
人間にとって読み書きが容易で、マシンにとっても簡単にパースや生成を行なえる形式です。
JavaScriptプログラミング言語 (ECMA-262標準第3版 1999年12月)の一部をベースに作られています。
JSONは完全に言語から独立したテキスト形式ですが、C、C++、C#、Java、JavaScript、Perl、Python、その他多くのCファミリーの言語を使用するプログラマにとっては、馴染み深い規約が使われています。
これらの性質が、 JSONを理想的なデータ交換言語にしています。

https://www.json.org/json-ja.html
とのことです。

基本ルール

RFC 8259にいろいろ書いてありました。
https://tools.ietf.org/html/rfc8259

値の種類

説明 書き方
object nameとvalueのペア { "name" : value }
arry 配列(タイプの異なる値もOK) [ value, value ]
number 数字(10進数) value
string 文字列 "value"
その他 true, false, null (小文字必須 trueなど

※複数の値を並べて書くときは、「,」で区切る

sample
      {
        "Image": {
            "Width":  800,
            "Height": 600,
            "Title":  "View from 15th Floor",
            "Thumbnail": {
                "Url":    "http://www.example.com/image/481989943",
                "Height": 125,
                "Width":  100
            },
            "Animated" : false,
            "IDs": [116, 943, 234, 38793]
          }
      }

※自分で書くと「,」を忘れたり、消し忘れたりしがち。
jsonフォーマッターとかで構文間違いがないか要確認。

2.OCI cliでjsonを使う

2-1.input情報にjsonを使う

oci cliを使う際のinput情報としてjson形式が求められることがあります。
また、オプション以下をまとめてjsonで記載してコマンドへ渡すこともできます。
※inputの場合でもWindowsでoci cliを使う場合の注意!が該当します

直書きするパターン

簡単なものであれば、直書きすることも(?)。
ただ、慣れてないとけっこう間違えてストレスです。

直書きパターン
$ oci network route-table create --route-rules '[{"cidrBlock":"0.0.0.0/0","networkEntityId":"ocid1.internetgateway.oc1.phx.xxxxxx"}]'

jsonファイルを渡すパターン

--from-jsonオプションでファイルに書いたものを渡す。
ファイルの場所はfile://に続けて絶対あるいは相対パスで指定。
同じサービス/リソースを何度も作る場合などは便利。
ただ、変数化して値を変えながら作るとかするなら、terraformとか使った方が楽。

ファイル渡しパターン
$ cat input.json
{
  "accessType": "ObjectRead",
  "bucketName": "test",
  "name": "test",
  "namespace": "test",
  "objectName": "test"
}
$ oci os preauth-request create --from-json file://input.json

inputに必要なjsonの型(skeleton)を確認する1

--generate-full-command-json-input

このオプションを付けると、そのコマンドのinputに必要な情報の全てを含んだ型をjson形式で出力してくれます。
出力結果をリダイレクトでファイルに吐き出してあげれば、あとはこれを埋めるだけでinput用のjsonファイルが完成します。jsonは形式が崩れて、構文エラーになることが多いので、こちらから作ると失敗が少ないかと。

必要なinput情報をjsonフォーマットで全部教えてくれる
$ oci network route-table create --generate-full-command-json-input
{
  "compartmentId": "string",
  "definedTags": {
    "string1": {
      "string1": {
        "string1": "string",
        "string2": "string"
      },
      "string2": {
        "string1": "string",
        "string2": "string"
      }
    },
    "string2": {
      "string1": {
        "string1": "string",
        "string2": "string"
      },
      "string2": {
        "string1": "string",
        "string2": "string"
      }
    }
  },
  "displayName": "string",
  "dn": "string",
  "freeformTags": {
    "string1": "string",
    "string2": "string"
  },
  "maxWaitSeconds": 0,
  "routeRules": [
    {
      "cidrBlock": "string",
      "destination": "string",
      "destinationType": "string",
      "networkEntityId": "string"
    },
    {
      "cidrBlock": "string",
      "destination": "string",
      "destinationType": "string",
      "networkEntityId": "string"
    }
  ],
  "vcnId": "string",
  "waitForState": "PROVISIONING|AVAILABLE|TERMINATING|TERMINATED",
  "waitIntervalSeconds": 0
}

inputに必要なjsonの型(skeleton)を確認する2

--generate-param-json-input

先ほどのオプションは、全input情報をjsonで書くための型を出力しますが、こちらは一部分のみ指定して、jsonの型を出力させられます。

input情報の一部分だけjsonフォーマットの型で教えてもらえる
$ oci network route-table create  --generate-param-json-input route-rules
[
  {
    "cidrBlock": "string",
    "destination": "string",
    "destinationType": "string",
    "networkEntityId": "string"
  },
  {
    "cidrBlock": "string",
    "destination": "string",
    "destinationType": "string",
    "networkEntityId": "string"
  }
]

2-2.outputのjsonを扱う

"--query"オプションを理解する

※先に「実際にjson outputを制御してみる」を見た方がイメージがわきやすいかもしれません。

getやlistの出力結果の型を理解する

"--query"オプションは、JMESPath のクエリを使うことで、出力結果をフィルタリングするオプションです。
最初はややとっつきにくいのですが、型を理解すると簡単です。
※今回はoci cliで使うという観点で。

getで出力されるjsonの型

例えば、oci cliのget(特手のリソースを指定して、詳細な情報を取得するコマンド)の結果は以下のような形で返ってきます。一見複雑ですが、ただのオブジェクト{ }の組み合わせです。

{ #O1
  "data": { #O2
    "XXXXX": "XXXXX",
    "XXXXX": "XXXXX",
    "XXXXX": { #O3
      "XXXXX": {  #O4a
        "XXXXX": "XXXXX"
      },          #O4aここまで
      "XXXXX": {  #O4b
        "XXXXX": "XXXXX"
      }           #O4bここまで
    },         #O3ここまで
    "XXXXX": "XXXXX",
    "XXXXX": "XXXXX",
    "XXXXX": XXXXX
  },        #O2ここまで
  "XXXXX": "XXXXX"
} #01ここまで

※O = Object として階層番号を振ってみました

{ "data" : {} }という name : value のペアがベースになっていて、valueの方も{ "XXXXX" : "XXXXX" }とペアになっていて、さらにこのオブジェクトのvalueも{ }とペアになっていて…と入れ子になっています。
イメージ:{ #O1 "data" : { #O2 "XXX" : {#O3 "XXX" : { #O4a "XXX" : "XXX" }, "XXX" : { #O4b "XXX" : "XXX" } } } }
※正確にはO1の中も、{ "data" : {}, "XXXXX" : "XXXXX" }と二つのペアが並んでいる。

listで出力されるjsonの型

listの結果もほぼ同じですが、listの場合は、複数のリソースについての情報を一覧にするコマンドなので、配列が使われています。

{ #O1
  "data": [ #A1
    {  #O2a
      "XXXXX": "XXXXX",
      "XXXXX": "XXXXX",
      "XXXXX": XXXXX
    }, #O2aここまで
    {  #O2b
      "XXXXX": "XXXXX",
      "XXXXX": "XXXXX",
      "XXXXX": XXXXX
    }, #O2bここまで
    {  #O2c
      "XXXXX": "XXXXX",
      "XXXXX": "XXXXX",
      "XXXXX": XXXXX
    }  #02cここまで
  ]          #A1ここまで
} #O1ここまで

※A = Arrayとして階層番号を振ってみました

こちらの場合は`{ "data" : [ ] }というオブジェクトがベースにありつつ、配列の中に[ { #O2a }, { #O2b }, { #O2c } ]といった形で、オブジェクトが並んでいます。

queryオプションでの指定の仕方を理解する

上の構造がわかれば、今度は指定の仕方です。

getの出力から一部の情報を指定する方法

例えば、以下のような出力の中から、"aaaaa"を取り出したい場合。
"O1"の「data」の中の、"AAAAA"(name)に対応する値(value)が欲しいので、"data.AAAAA"という形で指定します("aaaaa"が結果として返ってきます)。

{ #O1
  "data": { #O2
    "AAAAA": "aaaaa",
    "XXXXX": "XXXXX"
  }        #O2ここまで
} #01ここまで
listの出力から一部の情報を指定する方法

一方、listの出力結果で以下のような形となっている場合、複数のリソースが一覧になっているので、各々のリソースが同じ項目を持っています。

例えば、以下の出力から、各リソースの"AAAAA"に対応する値のみを一覧で表示したい場合。
"O1"の「data」の中の、配列(A1)の中の全てのリソース[*]から、"AAAAA"に対応する値が欲しいので、data[*].AAAAAという形で指定します("aaaaa"と"yyyyy"が結果として返ってきます)。
※例えば"AAAAA"が、リソース名であったり、IPアドレスであったりすると想像してください。

{ #O1
  "data": [ #A1
    {  #O2a
      "AAAAA": "aaaaa",
      "BBBBB": 10
    }, #O2aここまで
    {  #O2b
      "AAAAA": "yyyyy",
      "BBBBB": 100
    }  #O2bここまで
  ]          #A1ここまで
} #O1ここまで
応用

ここまで理解できれば、応用していくだけです。

  • 特定のリソースの、特定の情報

    例えば、「全てのリソース」ではなく、特定の条件に当てはまるリソースの、"AAAAA"に対応する値が欲しい場合。より具体的には、"BBBBB"が10より大きいリソースに関してのみ、"AAAAA"に対応する値を一覧で表示したい場合。
    data[?"BBBBB" >10].AAAAAとなります。先ほどはdata[*]と配列の中は何も指定していなかった箇所で、条件を指定している点に注意してください。

  • 複数の情報
    "AAAAA"だけでなく、"BBBBB"に対応する値も欲しい場合。この場合は、"data"に対する出力が複数になるので、配列を使って、data[*].["AAAAA","BBBBB"]という形になります。

  • nameとvalueのセットを表示
    ここまで"AAAAA"に対応する値のみを出力するようなフィルタをしてきましたが、"AAAAA":"aaaaa"といった形で、nameの方も出力させたい場合は、"data.AAAAA:AAAAA"とします。

階層がさらに深い場合は「.」を追加していけばOkです。

Windowsでoci cliを使う場合の注意!

Windowsの場合は、フィルタを行う際に注意が必要です。
ここはクラウドベンダのcliによって方言があるようです。
※参考:AWS Command Line Interface のパラメータ値の指定

ociの場合はマニュアルに以下の記載があります。

On Windows, to pass complex input to the CLI as a JSON string, you must enclose the entire block in double quotes. 
Inside the block, each double quote for the key and value strings must be escaped with a backslash (\) character.

The following command shows how to pass two values for the --metadata object on Windows.
oci os bucket create -ns mynamespace --name mybucket --metadata "{\"key1\":\"value1\",\"key2\":\"value2\"}" --compartment-id ocid1.compartment.oc1..aaaaaaaarhifmvrvuqtye5q66rck6copzqck3ukc5fldrwpp2jojdcypxfga

つまりWindowsの場合、以下のルールに従う必要があります。
1. ""(ダブルクォーテーション)で全てのブロックを囲う
2. ブロック内の"(ダブルクォーテーション)は、\(バックスラッシュ)でエスケープする

したがって、例えばdata[*].AAAAAと記載していたものは"data[*].AAAAA"と記載する必要があり、data[*]."AAAAA"と記載していたものは、"data[*].\"AAAAA\""と記載する必要があります。

※アンダースコア(_)以外の記号を使っている場合は、その値は""で囲う必要があります。

  • bashなどの場合
    • 全体をシングルクォーテーションで囲うことで""をエスケープ('data[*]."display-name"')
  • Windows PowerShellの場合
    • "\"(バックスラッシュ)に"`"(バッククォート)を加えた"\`"で""をダブルエスケープ("data[*].\`"display-name\`"")

# "Parameter '<PARAMETER NAME>' must be in JSON format."エラーが出続けて途方に暮れてたのですが、これが原因でした。

※マニュアルに書いてあるのと、失敗した時のエラーメッセージで教えてくれます。
- Managing CLI Input and Output
- エラーメッセージ(抜粋)

If a key name has any characters besides [a-z, A-Z, 0-9, _], it needs to be escaped.
In bash or similar "NIX" based shells used in "NIX" environment, escaping can be done byusing double quotes inside single quotes.
e.g. --query 'data[*]."display-name"'
If using PowerShell in Windows environment, escaping can be done by using double quoteswith double escape character \`.
e.g. --query data[*].\`"display-name\`"

実際にjson outputを制御してみる

やっとです。
※Windows PowerShellから実行

特定のcompute instanceの情報をgetする

何もフィルタしないとこんな感じ

フィルタ無しでgetする

> oci compute instance get --instance-id ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxx
{
  "data": {
    "availability-domain": "xxxx:PHX-AD-1",
    "compartment-id": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxx",
    "defined-tags": {
      "test_tag": {
        "tag1": "test"
      },
      "test_tag2": {
        "tag2": "test"
      }
    },
    "display-name": "test-instance",
    "extended-metadata": {},
    "fault-domain": "FAULT-DOMAIN-1",
    "freeform-tags": {},
    "id": "ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxx",
    "image-id": "ocid1.image.oc1.phx.xxxxxxxxxxxxxxxxxxx",
    "ipxe-script": null,
    "launch-mode": "EMULATED",
    "launch-options": {
      "boot-volume-type": "IDE",
      "firmware": "BIOS",
      "is-consistent-volume-naming-enabled": null,
      "is-pv-encryption-in-transit-enabled": null,
      "network-type": "E1000",
      "remote-data-volume-type": "SCSI"
    },
    "lifecycle-state": "STOPPED",
    "metadata": {},
    "region": "phx",
    "shape": "VM.Standard1.1",
    "source-details": {
      "boot-volume-size-in-gbs": null,
      "image-id": "ocid1.image.oc1.phx.xxxxxxxxxxxxxxxxxxx",
      "kms-key-id": null,
      "source-type": "image"
    },
    "time-created": "2019-01-16T11:22:54.286000+00:00",
    "time-maintenance-reboot-due": null
  },
  "etag": "xxxxxxxxxxxxxxxxxxx"
}

ここから、インスタンスの名前(display-name)とshape、リージョンとフォルトドメインのみを取得したい場合は以下。

フィルタしながらgetする

> oci compute instance get --instance-id ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxx --query "data.[\`"display-name\`", shape, region, \`"fault-domain\`"]"
[
  "test-instance",
  "VM.Standard1.1",
  "phx",
  "FAULT-DOMAIN-1"
]

複数のcompute instanceの情報をlistする

何もフィルタしないとこんな感じ。
※コンパートメントの指定はしています

フィルタ無しでlistする

> oci compute instance list
{
  "data": [
    {
      "availability-domain": "gype:PHX-AD-1",
      "compartment-id": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxx",
      "defined-tags": {
        "test_tag": {
          "tag1": "test"
        },
        "test_tag": {
          "tag2": "test"
        }
      },
      "display-name": "test-instance",
      "extended-metadata": {},
      "fault-domain": "FAULT-DOMAIN-1",
      "freeform-tags": {},
      "id": "ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxx",
      "image-id": "ocid1.image.oc1.phx.xxxxxxxxxxxxxxxxxxx",
      "ipxe-script": null,
      "launch-mode": "EMULATED",
      "launch-options": {
        "boot-volume-type": "IDE",
        "firmware": "BIOS",
        "is-consistent-volume-naming-enabled": null,
        "is-pv-encryption-in-transit-enabled": null,
        "network-type": "E1000",
        "remote-data-volume-type": "SCSI"
      },
      "lifecycle-state": "STOPPED",
      "metadata": {},
      "region": "phx",
      "shape": "VM.Standard1.1",
      "source-details": {
        "boot-volume-size-in-gbs": null,
        "image-id": "ocid1.image.oc1.phx.xxxxxxxxxxxxxxxxxxx",
        "kms-key-id": null,
        "source-type": "image"
      },
      "time-created": "2019-01-16T11:22:54.286000+00:00",
      "time-maintenance-reboot-due": null
    },
    {
      "availability-domain": "gype:PHX-AD-1",
      "compartment-id": "ocid1.compartment.oc1..YYYYYYYYYYYYYYYYY",
      "defined-tags": {
        "test_tag": {
          "tag1": "test"
        }
      },
      "display-name": "test-instance2",
      "extended-metadata": {},
      "fault-domain": "FAULT-DOMAIN-1",
      "freeform-tags": {},
      "id": "ocid1.instance.oc1.phx.YYYYYYYYYYYYYYYYY",
      "image-id": "ocid1.image.oc1.phx.YYYYYYYYYYYYYYYYY",
      "ipxe-script": null,
      "launch-mode": "NATIVE",
      "launch-options": {
        "boot-volume-type": "PARAVIRTUALIZED",
        "firmware": "UEFI_64",
        "is-consistent-volume-naming-enabled": true,
        "is-pv-encryption-in-transit-enabled": true,
        "network-type": "VFIO",
        "remote-data-volume-type": "PARAVIRTUALIZED"
      },
      "lifecycle-state": "STOPPED",
      "metadata": {
        "ssh_authorized_keys": "ssh-rsa YYYYYYYYYYYYYYYYY",
        "user_data": "XXX"
      },
      "region": "phx",
      "shape": "VM.Standard2.1",
      "source-details": {
        "boot-volume-size-in-gbs": null,
        "image-id": "ocid1.image.oc1.phx.YYYYYYYYYYYYYYYYY",
        "kms-key-id": null,
        "source-type": "image"
      },
      "time-created": "2018-12-31T02:27:32.144000+00:00",
      "time-maintenance-reboot-due": null
    },
    {
      "availability-domain": "gype:PHX-AD-1",
      "compartment-id": "ocid1.compartment.oc1..ZZZZZZZZZZZZZZZ",
      "defined-tags": {},
      "display-name": "instance-20190128-0100",
      "extended-metadata": {},
      "fault-domain": "FAULT-DOMAIN-3",
      "freeform-tags": {},
      "id": "ocid1.instance.oc1.phx.ZZZZZZZZZZZZZZZ",
      "image-id": "ocid1.image.oc1.phx.ZZZZZZZZZZZZZZZ",
      "ipxe-script": null,
      "launch-mode": "NATIVE",
      "launch-options": {
        "boot-volume-type": "PARAVIRTUALIZED",
        "firmware": "UEFI_64",
        "is-consistent-volume-naming-enabled": true,
        "is-pv-encryption-in-transit-enabled": true,
        "network-type": "VFIO",
        "remote-data-volume-type": "PARAVIRTUALIZED"
      },
      "lifecycle-state": "RUNNING",
      "metadata": {
        "ssh_authorized_keys": "ssh-rsa ZZZZZZZZZZZZZZZ",
        "user_data": "ZZZZZZZZZZZZZZZ"
      },
      "region": "phx",
      "shape": "VM.Standard2.1",
      "source-details": {
        "boot-volume-size-in-gbs": null,
        "image-id": "ocid1.image.oc1.phx.ZZZZZZZZZZZZZZZ",
        "kms-key-id": null,
        "source-type": "image"
      },
      "time-created": "2019-01-27T16:01:26.157000+00:00",
      "time-maintenance-reboot-due": null
    }
  ]
}

これらのリソースの名前のみの一覧を取得する。

フィルタしながらlistする

> oci compute instance list --query "data[*].\`"display-name\`""
[
  "test-instance",
  "test-instance2",
  "instance-20190128-0100",
]

出力を見やすくする

jsonに対するフィルタを行いつつ、--outputオプションで"table"を指定することで最終出力結果をもう少し見やすくすることができます。

アウトプットを表形式にする

> oci compute instance list --query "data[*].[\`"display-name\`", shape]" --output table
+--------------------------------+----------------+
| Column1                        | Column2        |
+--------------------------------+----------------+
| test-instance                  | VM.Standard1.1 |
| test-instance2                 | VM.Standard2.1 |
| instance-20190128-0100         | VM.Standard2.1 |
+--------------------------------+----------------+

列名(Column)がついていないのは、フィルタする際に省略していたからです。
省略せずに、nameとvalueのペアとして、フィルタを記載するとname部分が列名として出力されます。

表の列名を表示させたパターン

> oci compute instance list --query "data[*].{\`"display-name\`":\`"display-name\`", shape:shape}" -
-output table
+--------------------------------+----------------+
| display-name                   | shape          |
+--------------------------------+----------------+
| test-instance                  | VM.Standard1.1 |
| test-instance2                 | VM.Standard2.1 |
| instance-20190128-0100         | VM.Standard2.1 |
+--------------------------------+----------------+

3.TIPS

使っている中で気づいたことなどをメモ

  • sortやsort_byで値の並び変えはできるが、項目の並び替えは指定できない
    • 項目は0-9,A-Z昇順のようなので、表示名に「1.」や「2.」をつけることで制御可能
PS C:\> oci bv volume list --availability-domain "xxxx:US-ASHBURN-AD-1" --output table --query "data[*].{\`"1.availability-domain\`":\`"availability-domain\`", \`"2.display-name\`":\`"display-name\`", \`"3.size-in-mbs\`":\`"size-in-mbs\`",\`"4.volume-group-id\`":\`"volume-group-id\`"}"
+-----------------------+--------------------+---------------+-------------------+
| 1.availability-domain | 2.display-name     | 3.size-in-mbs | 4.volume-group-id |
+-----------------------+--------------------+---------------+-------------------+
| xxxx:US-ASHBURN-AD-1  | test-volume1       | 524288        | None              |
| xxxx:US-ASHBURN-AD-1  | test-volume2       | 524288        | None              |
| xxxx:US-ASHBURN-AD-1  | test-volume3       | 524288        | None              |
+-----------------------+--------------------+---------------+-------------------+
  • フェルタで指定する項目の名前を間違えると、エラーにはならず、値が"none"になる
    • 本当に値がnoneなのか、フィルタの項目名が間違っているのか要確認
PS C:\> oci bv volume list --availability-domain "xxxx:US-ASHBURN-AD-1" --output table --query "data[*].{\`"1.availability-domain\`":\`"availability-domain\`", \`"2.display-name\`":\`"display-name\`", \`"3.size-in-mbs\`":\`"size-in-mbs\`",\`"4.volume-group-id\`":\`"volume-group-id\`",\`"5.unexist item\`":\`"unexist item\`"}"
+-----------------------+--------------------+---------------+-------------------+----------------+
| 1.availability-domain | 2.display-name     | 3.size-in-mbs | 4.volume-group-id | 5.unexist item |
+-----------------------+--------------------+---------------+-------------------+----------------+
| xxxx:US-ASHBURN-AD-1  | test-volume1       | 524288        | None              | None           |
| xxxx:US-ASHBURN-AD-1  | test-volume2       | 524288        | None              | None           |
| xxxx:US-ASHBURN-AD-1  | test-volume3       | 524288        | None              | None           |
+-----------------------+--------------------+---------------+-------------------+----------------+
  • cliでAD名を指定する際はprefixまで付ける
    • AD名は、テナンシーごとにprefixがつくので、それを確認して指定する必要がある
    • ListAvailabilityDomainsのrest apiか、oci cliのoci iam availability-domain list、もしくはcomputeインスタンス作成画面のADを選択する箇所で確認可能

4.参考

以上

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5