ESXiでコマンドからVMを作成

はじめに

ESXiでのVMの作成を自動化したいので、そのための手順とコマンドを調査した。

まず結論

ESXiにsshでログインして、以下のようなスクリプトを実行すればよい。

#!/bin/sh -ue

# (0) パラメータ定義
DATASTORE_PATH=/vmfs/volumes/datastore1
ISO_FILE=/vmfs/volumes/datastore1/iso/CentOS-7-x86_64-DVD-1810.iso
VM_NAME=test-vm
VM_HW_VER=vmx-09
VM_MEM_SIZE=2048
VM_NETWORK_NAME="VM Network"
VM_GUEST_OS=centos-64
VM_CDROM_DEVICETYPE=cdrom-image  # cdrom-image / atapi-cdrom
VM_DISK_SIZE=20g
VM_DISK_PATH=$DATASTORE_PATH/$VM_NAME/$VM_NAME.vmdk
VM_VMX_FILE=$DATASTORE_PATH/$VM_NAME/$VM_NAME.vmx

# (1) ダミーVMの作成
VM_ID=`vim-cmd vmsvc/createdummyvm $VM_NAME $DATASTORE_PATH $VM_HW_VER`

# (2) vmxファイルの編集
sed -i -e '/^guestOS /d' $VM_VMX_FILE
cat << __EOF__ >> $VM_VMX_FILE
guestOS = "$VM_GUEST_OS"
memSize = "$VM_MEM_SIZE"
ethernet0.present = "TRUE"                                       
ethernet0.networkName = "$VM_NETWORK_NAME"                             
ethernet0.addressType = "generated"                              
ethernet0.wakeOnPcktRcv = "FALSE"                                
ide0:0.present = "TRUE"                         
ide0:0.deviceType = "$VM_CDROM_DEVICETYPE"                                
ide0:0.fileName = "$ISO_FILE"
__EOF__

# (3) ディスク容量の拡張
vmkfstools -X $VM_DISK_SIZE $VM_DISK_PATH 

# (4) VMの情報のリロード
vim-cmd vmsvc/reload $VM_ID

以降で各ステップの説明を行う。

(0) パラメータ定義

各種パラメータを変数化しているだけ。

(1) ダミーVMの作成

コマンドvim-cmd vmsvc/createdummyvmを実行して、最小限(ですらない)ダミーのVMを作成し、それのvmxファイルを編集するという手順らしい。 ちなみに、別のコマンドであるPowerCLIにはNew-VMというコマンドレットがあり、ダミーなしで一発でVMを作成できるようだが、残念ながら無償のESXiでは利用できない。 コマンドvim-cmd vmsvc/createdummyvmの使い方は、以下のようにヘルプで確認できる。

$ vim-cmd help vmsvc/createdummyvm
Usage: createdummyvm vm_name datastore_path [hw_version]

Create a pre-configured dummy vm.

パラメータhw_versionには、コマンドvim-cmd solo/querycfgoptdescの出力のkeyの値で、なおかつcreateSupported = trueであるものを指定すればよいようだ。 例えば以下の出力結果の場合は、vmx-04とvmx-07~vmx-14が指定可能である。

$ vim-cmd solo/querycfgoptdesc
(vim.vm.ConfigOptionDescriptor) [
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-03", 
      description = "ESX 2.x virtual machine", 
      host = <unset>, 
      createSupported = false, 
      defaultConfigOption = false, 
      runSupported = false, 
      upgradeSupported = false
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-04", 
      description = "ESX 3.x virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-07", 
      description = "ESX/ESXi 4.x virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-08", 
      description = "ESXi 5.0 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-09", 
      description = "ESXi 5.1 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-10", 
      description = "ESXi 5.5 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-11", 
      description = "ESXi 6.0 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-12", 
      description = "Workstation 12 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-13", 
      description = "ESXi 6.5 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = false, 
      runSupported = true, 
      upgradeSupported = true
   }, 
   (vim.vm.ConfigOptionDescriptor) {
      key = "vmx-14", 
      description = "ESXi 6.7 virtual machine", 
      host = <unset>, 
      createSupported = true, 
      defaultConfigOption = true, 
      runSupported = true, 
      upgradeSupported = true
   }
]

(2) vmxファイルの編集

createdummyvmの実行で、以下の内容のvmxファイルが生成された。

これに対して、ゲストOSとメモリサイズを指定し、NICとCD-ROMドライブを追加。 vmxファイルを編集するためのコマンドはないようなので、 直接編集する。

(3) ディスク容量の拡張

ダミーVMのハードディスクの容量は1MBのサイズしかないので、20GBに拡張しておく。

(4) VMの情報のリロード

vmxファイルを編集したり、ディスクを拡張したりしても、ESXiが保持している管理情報は元のままになっている。 そのため、コマンドvim-cmd vmsvc/reloadを利用して管理情報をリロード(更新)しておく。

ESXi 6.7のパッチの適用手順

はじめに

ESXi 6.7にパッチを適用した際の作業記録を記しておく。 前提となるESXiホストの環境は以下とする。

項目 状態
バージョン 6.7 (パッチ未適用)
IPアドレス 192.168.8.20
ssh接続 enabled

ESXiはVMそのものは安定しているが、周辺ツールにバグが多い印象があり、パッチは適用しておきたい。 しかし、VMWare固有のモジュール管理の仕組みを理解する必要があり、少々ハードルが高い。

用語

VMWareのモジュール管理で利用される用語を理解しておく必要がある。

  • VIB (VMware Infrastructure Bundle)

  • プロファイル

    • 特定の構成のESXiであり、必要なVIBをグループ化したもの。 例えば、「標準のESXi」や「VMWare toolsを含まないESXi」など
  • デポ

    • ひとつ又は複数のプロファイルをまとめてzipファイルにしたもの。 いわゆる「パッチ」はこのzipファイルのこと。

より正確な定義については、以下を参照。

パッチのダウンロード

以下のWebサイトからパッチをダウンロード。

製品は「ESXi (Embedded and Installable)」で、バージョンは「6.7.0」で検索。 執筆時点の最新版はESXi670-201811001で、ESXi670-201811001.zipというファイル名でダウンロードされる。 ちなみに、ハードウェアベンダがプリインストールするものがEmbedded、ユーザがダウンロードしてインストールするものがInstallableらしい。

パッチをデータストアに転送

ここでは以下のようにscpで転送する。

$ scp ESXi670-201811001.zip root@192.168.8.20:/vmfs/volumes/datastore1

データストアブラウザを利用して転送してもよい。

ESXiホストにsshで接続

$ ssh root@192.168.8.20

現在のプロファイルの確認

$ esxcli software profile get
ESXi-6.7.0-8169922-standard
   Name: ESXi-6.7.0-8169922-standard
   Vendor: VMware, Inc.
   Creation Time: 2018-12-15T11:38:01
   Modification Time: 2018-12-15T11:38:31
   Stateless Ready: True
   Description: 
      
      The general availability release of VMware ESXi Server 6.7.0
      brings whole new levels of virtualization performance to
      datacenters and enterprises.

   VIBs: ata-libata-92 3.00.9.2-16vmw.670.0.0.8169922, (以下略)

ここでは、現在のプロファイルが「ESXi-6.7.0-8169922-standard」であることが分かる。

パッチの内容の確認

まずはパッチ(デポ)に含まれるプロファイルのリストを取得する。

$ esxcli software sources profile list -d /vmfs/volumes/datastore1/ESXi670-201811001.zip 
Name                             Vendor        Acceptance Level  Creation Time        Modification Time
-------------------------------  ------------  ----------------  -------------------  -------------------
ESXi-6.7.0-20181104001-no-tools  VMware, Inc.  PartnerSupported  2018-11-08T08:39:27  2018-11-08T08:39:27
ESXi-6.7.0-20181104001-standard  VMware, Inc.  PartnerSupported  2018-11-08T08:39:27  2018-11-08T08:39:27

名前がstandardで終わっているものが標準構成、no-toolsで終わっているものがVMWare tools (のISOイメージ?)を含まない構成。 プロファイルの命名規則などについては以下を参照。

今回は標準構成のESXi-6.7.0-20181104001-standardを適用することにする。 ESXi-6.7.0-20181104001-standardの詳細な内容は、以下のようにコマンドを実行することで確認できる。

$ esxcli software sources profile get -p ESXi-6.7.0-20181104001-standard -d /vmfs/volumes/datastore1/ESXi
670-201811001.zip
ESXi-6.7.0-20181104001-standard
   Acceptance Level: PartnerSupported
   Name: ESXi-6.7.0-20181104001-standard
   Vendor: VMware, Inc.
   Creation Time: 2018-11-08T08:39:27
   Modification Time: 2018-11-08T08:39:27
   Stateless Ready: True
   Description: 
      
      Updates ESXi 6.7 Image Profile-ESXi-6-7-0-20181104001-standard

   VIBs: ata-libata-92 3.00.9.2-16vmw.670.0.0.8169922, (以下略)

パッチの適用

まずは、メンテナンスモードに移行する。

$ vim-cmd hostsvc/maintenance_mode_enter

そして、プロファイルESXi-6.7.0-20181104001-standardを適用する。

$ esxcli software profile update -d /vmfs/volumes/datastore1/ESXi670-201811001.zip -p ESXi-6.7.0-20181104001-standard
Update Result
   Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
   Reboot Required: true
   VIBs Installed: VMW_bootbank_bnxtroce_20.6.101.0-20vmw.670.1.28.10302608, (以下略)
   VIBs Removed: VMW_bootbank_brcmfcoe_11.4.1078.0-8vmw.670.0.0.8169922, (以下略)
   VIBs Skipped: VMW_bootbank_ata-libata-92_3.00.9.2-16vmw.670.0.0.8169922, (以下略)

ここで、出力結果で「Reboot Required: true」となっているので、リブートを実行する。

$ reboot

再度sshでESXiのホストに接続し、メンテナンスモードを解除する。

$ vim-cmd hostsvc/maintenance_mode_exit

確認

現在のプロファイルを表示して、アップデートされていることを確認する。

$ esxcli software profile get
(Updated) ESXi-6.7.0-20181104001-standard
   Name: (Updated) ESXi-6.7.0-20181104001-standard
   Vendor: VMware, Inc.
   Creation Time: 2018-12-16T17:42:02
   Modification Time: 2018-12-16T17:42:18
   Stateless Ready: True
   Description: 
      
      2018-12-16T17:42:01.737859+00:00: The following VIBs are
      installed:
        vmkusb        0.1-1vmw.670.1.28.10302608
        vsan  6.7.0-1.31.10720746
        (略)
        lsi-mr3       7.702.13.00-5vmw.670.1.28.10302608
        esx-ui        1.30.0-9946814
      ----------
      The general availability release of VMware ESXi Server 6.7.0
      brings whole new levels of virtualization performance to
      datacenters and enterprises.

   VIBs: ata-libata-92 3.00.9.2-16vmw.670.0.0.8169922, (以下略)

参考

KVMを利用してESXi 6.7のVMを構築する手順

はじめに

実験のためにESXi 6.7の環境が必要になったので、KVMを利用してESXi 6.7のVMを構築した。 ここでは、その構築手順を記しておく。 と言っても、KVM固有の手順はあまりなく、ほぼ単にESXiを構築する手順だが。

1. ESXiのISOイメージのダウンロード

以下のWebサイトからダウンロード。 ユーザアカウント(無料)を作成して、ログインする必要がある。

執筆時点での最新版はESXi 6.7.0。 ダウンロードのページで無償利用のためのライセンスキーも表示されるので、控えておく。

2. KVMホストのBIOSIntel VTを有効化

入れ子の仮想化(nested virtualization)になるので、KVMホストのBIOSIntel VTを有効化しておく。 理由は分からないけど、サーバマシンを含めて大抵のマシンではデフォルトではIntel VTは無効化されている。

3. Virt ManagerでESXiの仮想マシンを作成

ここではVirt Managerを利用してESXiの仮想マシンを作成する。 virshでもできるとは思う。

3.1. インストール方法を選択

f:id:redj:20181215232817p:plain

3.2. ESXiのISOイメージを選択

f:id:redj:20181215233052p:plain

3.3. メモリとCPUを指定

f:id:redj:20181215233954p:plain

3.4. ディスクサイズを指定

f:id:redj:20181215234107p:plain

3.5. 名前の指定とカスタマイズの指定

NICを変更する必要があるため、「インストールの前に設定をカスタマイズする」にチェックを入れておく。

f:id:redj:20181215234408p:plain

3.6. NICの変更

NICの設定で「デバイスのモデル」を「e1000」に変更する。 その他のモデルではESXiがNICを認識できなかった。

f:id:redj:20181215235051p:plain

そして「インストールの開始」をクリック。

4. ESXiのインストール

4.1. ESXiのインストールの開始

VMが起動するとブートメニューが表示されるので、「ESXi-6.7.0-8169922-standard Installer」を選択する。

f:id:redj:20181215235829p:plain

4.2. ウェルカム画面

f:id:redj:20181216000021p:plain

4.3. ライセンス許諾

f:id:redj:20181216000311p:plain

4.4. インストール先のディスクの選択

ここではデフォルトのままにしておく。

f:id:redj:20181216000640p:plain

4.5. キーボードレイアウトの選択

「Japanese」を選択する。

f:id:redj:20181216000752p:plain

4.6. 警告の表示

ふたつ警告が出た。

f:id:redj:20181216001827p:plain

ひとつは、VMに割り当てている仮想CPUでIntel VTが有効化されていない(そもそもできる?)ために出ていると思われる。 VMBIOSにはSeaBIOSというものが使われているようだが、軽くググったところ設定変更はBIOSのリビルドが必要っぽく感じる。 物理CPUのIntel VTは有効化しているので、とりあえず無視する。

もうひとつは、非推奨のNICであるIntel e1000が原因で出ている。 非推奨とはいえ現状では一応動いているし、他のNICはESXiが認識しなかったので、この警告も無視することにする。

4.7. インストール実行の確認

f:id:redj:20181216003600p:plain

4.8. インストール完了画面

Enterを押下するとリブートする。

f:id:redj:20181216003820p:plain

5. ESXiの設定

5.1. ESXiの初期画面

起動が完了すると以下の画面が表示される。 設定変更を行うので、画面左下にあるとおりF2を押下する。

f:id:redj:20181216004123p:plain

5.2. Troubleshooting Optionsを選択

f:id:redj:20181216004349p:plain

5.3. ESXi ShellとSSHを有効化

f:id:redj:20181216004525p:plain

ESCを押下して前の画面に戻る。

5.4. Configure Management Networkを選択

f:id:redj:20181216004733p:plain

5.5. IPv4 Configurationを選択

f:id:redj:20181216004835p:plain

5.6. IPv4の設定

インストール直後のESXiはDHCPを利用しているので、静的IPに変更する。

f:id:redj:20181216005344p:plain

ESCを押下して前の画面に戻る。

5.7. IPv6 Configurationを選択

f:id:redj:20181216005520p:plain

5.8. IPv6の無効化

余計なトラブル防止のため、IPv6は無効化しておく。

f:id:redj:20181216005724p:plain

ESCを押下して前の画面に戻る。

5.9. DNS Configurationを選択

f:id:redj:20181216010152p:plain

5.10. DNSとホスト名の指定

DHCPを利用しないので、DNSとホスト名を静的に指定する。

f:id:redj:20181216010246p:plain

ESCを押下して前の画面に戻る。

5.11. リブート

IPv6を無効化したので、リブートを求められる。

f:id:redj:20181216010451p:plain

コンソールからの設定はこれで完了。

6. SSHの鍵認証の設定

ESXiのホストに鍵認証でSSHできるようにしておく。

6.1. KVMのホスト側からsshで接続

$ ssh root@192.168.8.20  ←● sshコマンド実行
The authenticity of host '192.168.8.20 (192.168.8.20)' can't be established.
RSA key fingerprint is SHA256:KkaJl6NMquDYlPwbqA7AsmuefrZF5uweLZv5xEtAV2E.
Are you sure you want to continue connecting (yes/no)? yes  ←● yesを入力
Warning: Permanently added '192.168.8.20' (RSA) to the list of known hosts.
Password:  ←● パスワードを入力
The time and date of this login have been sent to the system logs.

WARNING:
   All commands run on the ESXi shell are logged and may be included in
   support bundles. Do not provide passwords directly on the command line.
   Most tools can prompt for secrets or accept them from standard input.

VMware offers supported, powerful system administration tools.  Please
see www.vmware.com/go/sysadmintools for details.

The ESXi Shell can be disabled by an administrative user. See the
vSphere Security documentation for more information.
[root@esxi:~]  ←● ESXiのホストのプロンプトが表示された

6.2. SSHのauthorized_keysの登録

rootのauthorized_keysは以下のファイルなので、ここにSSHで利用する公開鍵を追加しておく。

  • /etc/ssh/keys-root/authorized_keys

あとは任意でKVMホスト側などで~/.ssh/configに以下のエントリを追加しておく。

Match host 192.168.8.20 user root
  IdentityFile /path/to/private-key

7. ライセンス登録

ESXiに無償利用のライセンスを登録しておかないと、評価期間の60日が経過すると利用できなくなってしまう。 登録方法を2パターン記載しておく。

7.1. Embedded Host Clientからライセンス登録

以下のアドレスにブラウザでアクセスし、rootなどでログインする。

  • https://[ESXiホストのIP]

そして、以下のように画面を辿ってライセンスを登録する。

f:id:redj:20181216021324p:plain

7.2. コマンドラインからライセンス登録

sshでESXiホストに接続して、以下のコマンドを実行する。

$ vim-cmd vimsvc/license --set [ライセンスコード]

8. その他

8.1. Embedded Host Clientのキーボードレイアウトの変更

Embedded Host Clientのデフォルトのキーボドレイアウトは英語なので、日本語に変更する。 英語だとコンソールの操作がしんどい。 ESXi本体のキーボドレイアウトとは別の設定になっているようだ。

f:id:redj:20181216041709p:plain

参考

Node.jsの情報源

はじめに

Node.jsの勉強を始めたので、各種情報源をメモしておく。

Node.jsとその周辺

フレームワーク

  • Epress
    • https://expressjs.com/
    • Fast, unopinionated, minimalist web framework for Node.js
    • IBMが関係しているらしい
      • https://en.wikipedia.org/wiki/Express.js
      • In June 2014, rights to manage the project were acquired by StrongLoop. StrongLoop was acquired by IBM in September 2015; in January 2016, IBM announced that it would place Express.js under the stewardship of the Node.js Foundation incubator.

「踏み台」の英語表現

はじめに

踏み台についての英語の記事を検索したかったが、「踏み台」を英語でどう表現するか分からなかったので、調べてみた。

結論

どうもjump host、jump serverあるいはbastion hostなどと表記するらしい。

Wikipediaのjump hostの説明

https://en.wikipedia.org/wiki/Jump_server より。

A jump server, jump host or jumpbox is a computer on a network used to access and manage devices in a separate security zone. The most common example is managing a host in a DMZ from trusted networks or computers.

日本語訳は以下のようになる。

jump server、jump hostあるいはjumpboxは、異なるセキュリティゾーンに配置されている機器に対するアクセスや管理を行うための、ネットワーク上のコンピュータである。 よくある事例は、信頼できるネットワークやコンピュータからの、DMZに配置されているホストに対する管理である。

意味的に「踏み台」で合っていそう。

Wikipediaのbastion hostの説明

https://en.wikipedia.org/wiki/Bastion_host より。

A bastion host is a special purpose computer on a network specifically designed and configured to withstand attacks. The computer generally hosts a single application, for example a proxy server, and all other services are removed or limited to reduce the threat to the computer. It is hardened in this manner primarily due to its location and purpose, which is either on the outside of a firewall or in a demilitarized zone (DMZ) and usually involves access from untrusted networks or computers.

(略)

In an Amazon Web Services (AWS) context, a bastion host is defined as "a server whose purpose is to provide access to a private network from an external network, such as the Internet.

日本語に訳してみる。

bastion hostは、攻撃を防御するために設計および設定された、ネットワーク上の特別な目的のコンピュータである。 そのコンピュータは、一般的には例えばプロキシサーバのような単一のアプリケーションだけをホストしており、その他すべてのサービスはそのコンピュータへの脅威を減らすために削除されているか制限されている。 そのコンピュータの場所と目的、つまりファイアウォールの外側または非武装地帯(DMZ)であり、通常は信頼されていないネットワークまたはコンピュータからのアクセスに晒されるため、前述のような方法で強化されている。

(略)

Amazon Web Services (AWS)の文脈では、bastion hostは「インターネットのような外部のネットワークからプライベートネットワークへのアクセスを提供することが目的のサーバ」と定義されている。

bastion hostを直訳すると要塞ホストだが、確かに踏み台は要塞ホストの一種と言える。 ただ、日本語で「要塞ホスト」と書いてしまうと、「踏み台」という意味は読み取れなくなるな。

AWSではbastion hostは踏み台と思えばいいみたい。 なので、AWSのドキュメントを確認。 https://docs.aws.amazon.com/quickstart/latest/linux-bastion/architecture.htmlより。

Bastion Hosts

Including bastion hosts in your VPC environment enables you to securely connect to your Linux instances without exposing your environment to the Internet. After you set up your bastion hosts, you can access the other instances in your VPC through Secure Shell (SSH) connections on Linux.

日本語に訳してみる。

Bastion Hosts

VPC環境に踏み台(bastion host)を含めることで、環境をインターネットに晒すことなく、Linuxインスタンスに安全に接続できるようになる。 踏み台(bastion host)をセットアップした後は、Linux上のセキュアシェル(SSH)コネクションを通して、VPCにある他のインスタンスにアクセスできる。

補足だが、ここで言う「インスタンス」はEC2インスタンス、すなわち仮想マシンのこと。

[例文] Ansibleのドキュメント(FAQ)

https://docs.ansible.com/ansible/latest/reference_appendices/faq.html より。

How do I configure a jump host to access servers that I have no direct access to?

(略)

Note that ssh -W is available only with OpenSSH 5.4 or later. With older versions, it’s necessary to execute nc %h:%p or some equivalent command on the bastion host.

日本語に訳してみる。

直接アクセスできないサーバにアクセスするためには、どのように踏み台(jump host)を設定すればよいか?

(略)

ssh -WはOpenSSH 5.4以降でのみ利用できる点に注意。 古いバージョンでは、踏み台(bastion host)でnc %h:%pか、それと同等のコマンドを実行する必要がある。

ここでの「jump host」と「bastion host」の違いは、単に表記ゆれと思われる。 英語的には適切な文脈の中であればjump hostもbastion hostもそれほど大きな差はないのかもしれない。

プロキシの設定

はじめに

セキュリティ担保の都合でプロキシ通さないとインターネットに出られない環境では、プロキシの設定が原因でつまずく事が多い。

  • プロキシ除外にテストマシンを指定し忘れて、テスト失敗
  • インタラクティブシェルがどうのとかで、Ansibleの実行に失敗
  • Systemdのサービスへのhttp_proxyの設定し忘れで通信エラー
  • Google Chrome環境変数http_proxyを見てくれない事をすぐに忘れる
  • etc...

毎回同じようなことを調べているので、少しずつ整理していく。

Linux

GNOME

Google Chromeは、GNOMEのプロキシの設定を参照している。

Windows

そのうちまとめる。 WinHTTPとか。

Ansibleにおいて、リストの各要素をシングルクォートで囲んでからカンマで連結

はじめに

諸般の事情により、以下のようなリストについて、

terms:
  - AI
  - Artificial Intelligence

以下のように加工する必要に迫られた。

"'AI', 'Artificial Intelligence'"

つまり、それぞれの要素をシングルクォートで囲って、カンマで連結するとともに、全体をダブルクォートで囲むという加工を行う。 フィルタの使い方がややこしいのに加えて、ハマりどころもあるので、調査記録を残しておく。

最終的に出来上がったPlaybook

試行錯誤した結果、Playbookの内容は以下に落ち着いた。

- set_fact:
    single_quote: "'"

- shell: echo "{{ terms | map('regex_replace', '(^|$)', single_quote) | join(', ') }}"

モジュールdebugを利用して出力した結果の抜粋は以下。

ok: [192.168.8.8] => {
    "msg": "'AI', 'Artificial Intelligence'"
}

フィルタmapを利用することで、リストの各要素にフィルタregex_replaceを適用してシングルクォートで囲む加工を実現している。

また、置換後の文字列であるシングルクォートは、変数を利用して間接的に指定している。

ハマりどころ1 - シングルクォートを即値で表現できない

シングルクォートを即値で表現できないか色々試したが、どれもダメだった。 例えば、以下のようにシングルクォートを指定すると、文法エラーが発生する。

- shell: echo "{{ terms | map('regex_replace', '(^|$)', "'") | join(', ') }}"

全体をダブルクォートで囲まなければ文法エラーにはならないのだが、それでは望んだ結果は得られない。 バックスラッシュでエスケープできないか試したが、これもダメだった。 このもどかしさは、シェルスクリプトでありがちな、クォートやエスケープがネストすると訳が分からなくなるパターンと同じ。 一気に全部作ろうとせずに、タスクを分割して部分的に組み上げていくと、即値で書けるかもしれない。 しかし、中間的な作業変数は作りたくないので、シングルクォートの定数(みたいなもの)を準備することにした。

ハマりどころ2 - 標準のフィルタquote

標準でquoteというフィルタがあり、ぱっと見はこれで良さそうに思える。 フィルタquoteを利用したPlaybookは以下のようになる。

- shell: echo "{{ terms | map('quote') | join(', ') }}"

モジュールdebugを利用して出力した結果の抜粋は以下。

ok: [192.168.8.8] => {
    "msg": "AI, 'Artificial Intelligence'"
}

Artificial Intelligenceはシングルクォートで囲まれているのに、AIは囲まれていない。 調べてみると、Ansibleの問題報告に同じような事例Possible quote filter issue #28084を発見。 Ansibleの中の人のコメントは以下。

So the quote filter decides that a string with an asterisk needs to be quoted, but "/mnt" is fine without quotes. Which is correct for shell usage.

I don't think the quote-filter is what you need to use for your use-case as your intended output is not specific to the shell.

自分なりに意訳すると以下。

そのため、フィルタquoteは、アスタリスクを含む文字列についてはクォートが必要と判断しているが、"/mnt"についてはクォートがなくても問題は起きないと判断している。 フィルタquoteはシェルのために利用するので、これは正しい動作だ。

あなたのユースケースでは、出力結果はシェルでの利用を意図していないので、フィルタquoteは望んでいるものではないと思う。

ということで、フィルタquoteはシェルが解釈することを前提に、クォートするかどうかを独自に判断している。 そのため、フィルタquoteが利用できる状況はかなり限定される。 せめて強制的にクォートするようなフラグがあれば、使いようがあるのだが。

参考