彩り

私の作業が誰かの役に立つといいな。IT系のTech記事中心です。

続・Playbookで指定できる文字について の話

これは Ansible Advent Calendar 2019 の11日目の記事となります。

qiita.com

いつの間にかその2、その3もできてますね。

Ansible 2 Advent Calendar 2019 - Qiita

Ansible 3 Advent Calendar 2019 - Qiita

はじめに

今年の7月にAnsibleのコミュニティイベントでLT登壇の機会がありました。

speakerdeck.com

内容としては
・①network_cliプラグインの内部の動き
・②Playbookで指定できる文字についての話

と5分で2つの内容を取り扱ったのですが、やはり時間的に無理がありまして、どちらかの内容だけに特化すればよかったと後日反省しました。
network_cliプラグインについては自分としてはとっても好きな部分なので、この先またなんらかの形でアウトプットができればとも思うのですが、②についてはLTの内容があまりにも不完全燃焼だったので、この場を借りて本来もう少し親切に書きたかった部分についてまとめておこうと思います。

結論はLT内容と変わりません

事前準備の情報

モジュールオプションのlist型 について

今回「Playbookで指定できる文字」で触れる部分ですが、もっと具体的にいうと「list型」で指定できる文字種となります。
これは例えばネットワーク機器で状態取得系のコマンドを指定する ios_commandモジュールのcommandsオプションであったり、telnetモジュールのcommandオプションなどが使っている型となります。
ios_commandでは、 「List of commands to send to the remote ios device over~」
と説明もされていますね。
また、 ソース上ではtype='list'と指定している部分のオプションとなります。

    argument_spec = dict(
        commands=dict(type='list', required=True),

少し補足ですが、各モジュールが提供するオプションにはそれぞれ型があり、型によって指定できるパラメータが定義されます。
この部分は、int型choices型(選択式)bool型など色々とありますが、今回取り扱うlist型は複数のコマンドを指定する場合に使われる型なので、比較的馴染みがあるかと思います。
ちょっと癖のある、bool型などがYAMLフォーマット(Playbook)でどう扱われるか、についてはてくなべさんの記事でも取り上げられています。

tekunabe.hatenablog.jp ※上記エントリまとめ部分のYAMLフォーマットに対して指定した値がどう判断されるか

list型 をPlaybook内で指定する方法

話を戻して
今回取り扱うlist型をPlaybookに記載する方法についてですが、恐らく以下の3パターンかと思っています。

①そのまま記載

  - name: playbook pattern 1
    xxx_command:
      commands:
      - show version

②シングルコーテーション囲み

  - name: playbook pattern 2
    xxx_command:
      commands:
      - 'show version'

③ダブルコーテーション囲み

  - name: playbook pattern 3
    xxx_command:
      commands:
      - "show version"

本記事について

伝えたい内容

この3パターンについて、
・指定可能な文字種が異なり
・送信可能な場合においても微妙にエスケープ方法が異なります
というのが本記事で扱う内容となります。

普段あまり困らないかもしれませんが、複雑な表現(例えば正規表現など)をPlaybookで記載する時などにお役に立てば幸いかなと。

注意

Playbookで指定できる文字について、はansibleコマンドansible-playbookコマンドが実行できるまでの内容となります。
--syntax-checkを通すまでの部分
Playbookが正常に実行された後は、ターゲット(のシェル)がその文字を解釈できるのかという別の要素も絡んでくる為、本記事ではあくまで「Playbookで指定できる文字」についての内容となります。

f:id:naka-shin1:20191210201654p:plain

検証

検証環境

Ansible:2.9.0
Python :3.7.4
Module :ios_command

Playbook毎の使用文字種

それぞれの指定方法で、以下の文字列を指定して送信可能かどうかを確認しました
可視化文字列全てです。

!#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~

※大文字AとBの間に半角スペース含んでいます。

これをPlaybook化します。

①そのまま記載

    ios_command:
      commands:
        - !#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~

と、明らかにシンタックスハイライトの時点で通らなそうですよね --syntax-checkしても当然エラーとなります。

ERROR! Syntax Error while loading YAML.
  expected URI, but found '#'

The error appears to be in '/xxx/xxx/xxx/ios_command.yml': line 19, column 12, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

      commands:
        - !#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~
           ^ here

なんか#で怒られてますね。
前から順番に#を削っても"が怒られ、"を削っても&が怒られ、とこのパターンでは全ての文字種を送付するのは難しそうです。

②シングルコーテーション囲み

    ios_command:
      commands:
        - '!#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~'

これも、そのまま指定して実行するとエラーとなります。

The offending line appears to be:

      commands:
        - '!#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~'
                  ^ here

ですが、
シングルコーテーション'はシングルコーテーション'エスケープできるので、以下のように書くとシンタックスエラーを回避できます。
※分かりにくいかもしれませんが、7つ目に指定しているシングルコーテーションを2回書いています。
※ブログ上のシンタックスハイライトもエスケープ前と比較してそれらしくなっている気がします。

    ios_command:
      commands:
        - '!#"$%&''()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~'

③ダブルコーテーション囲み

    ios_command:
      commands:
        - "!#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~"

これもそのまま実行するエラーとなります。「②シングルコーテーション囲み」よりもエラーが激しく、きちんとエスケープしなさいと例まで出ています。

      commands:
        - "!#"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghIjklmnopqrstuvwxyz{|}~"
              ^ here
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:

    foo: "bad" "wolf"

Could be written as:

    foo: '"bad" "wolf"'

ダブルコーテーションは、バックスラッシュ\でダブルコーテーション"とバックスラッシュ\エスケープできるので、以下のように書くとシンタックスエラーを回避できます。

    ios_command:
      commands:
        - "!#\"$%&'()*+,-./0123456789:;<=>?@A BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghIjklmnopqrstuvwxyz{|}~"

まとめ

Playbookのlist型で指定できる文字種は以下の点に注意すれば可視化文字全部指定できます。

指定方法 ポイント
①そのまま指定 色々と送れない文字あり
②シングルコーテーション囲み シングルコーテーション'
シングルコーテーション'エスケープ
③ダブルコーテーション囲み ダブルコーテーション"とバックスラッシュ\
バックスラッシュ\エスケープ

シンタックスエラーではまってしまった時、私は、
「とりあえずシングルコーテーションで囲って、シングルコーテーションはシングルコーテーションでエスケープ」
する事で、多少複雑な指定をしてもPlaybook実行時やシンタックスチェックでハマらなくなりました。

という事で需要があるか分かりませんが、以上となります。