post-thumb

jq で XML を処理する

  • 所要時間 約 3 分
  • 14 Mar, 2022

この記事では、OVF の XML ファイルから OVF パラメータ一覧を出力するために使った xq コマンドを紹介します。 xmllint で挫折し、xq…というよりも jq を使った処理を説明します。

xpath を使えるコマンド

まず XML のノードを参照するために xpath を使うことのできるコマンドを探します。

「xpath コマンド」で検索すると、すぐに以下のコマンドが見つかります。

  • xmllint
  • xq

それぞれ試してみます。

xmllint との戦い

まず、xmllint から試してみます。対象とするの vCenter Server の OVF ファイル。

1
2
3
4
> xmllint --xpath "/ovf:Envelope" VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf
XPath error : Undefined namespace prefix
xmlXPathEval: evaluation failed
XPath evaluation failure

うまくいきません。どうも namespace のある XML のパースには、苦労するようです。 タグの名前を取得する関数 local-name() を駆使していける模様。 すべてのタグを対象として local-name() でフィルタリングする処理となります。

取得したい値は /ovf:Envelope/VirtualSystem/ProductSection/Property/@ovf:key というパスになります。

1
2
> xmllint --xpath '/*[local-name()="Envelope"]/*[local-name()="VirtualSystem"]/*[local-name()="ProductSection"]/*[local-name()="Property"]/@*[local-name()="key"]' VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf
 ovf:key="guestinfo.cis.appliance.net.addr.family" ovf:key="guestinfo.cis.appliance.net.mode" ovf:key="guestinfo.cis.appliance.net.addr" ovf:key="guestinfo.cis.appliance.net.prefix" ovf:key="guestinfo.cis.appliance.net.gateway" ovf:key="guestinfo.cis.appliance.net.dns.servers" ovf:key="guestinfo.cis.appliance.net.pnid" ...

なんとかパースできるようにするも、local-name() だらけという…流石にこれは使いたくない…。

どうにかならないかと もう少し調べてみると xmllint --shell というインタラクティブモードがありました。 インタラクティブモードの入力を標準入力で渡す方法が散見されますが、「コレジャナイ」感にあふれています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
string trim "
     setns ovf=http://schemas.dmtf.org/ovf/envelope/1
     cd /ovf:Envelope
     ls" | xmllint --shell VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf
/ > / > ovf:Envelope > ta-        3
---       11 References
ta-        3
-a-        5 vmw:MigrationUpgradeRequisitesSection
ta-        3
---        5 NetworkSection
ta-        3
-a-        3 vmw:IpAssignmentSection
ta-        3
---       71 DiskSection
ta-        3
---       33 DeploymentOptionSection
ta-        3
-a-       18 VirtualSystem
ta-        1
-a-        5 Strings
ta-        3
ovf:Envelope >

もっとこう jq 的に処理できないでしょうか…

xq を経て jq へ

XML, jq で検索すると xq というコマンドにたどり着きます。 xq を単純に実行すると XML を変換した JSON を取得できます。 xq で xpath 的に処理するのもいいのですが、変換した JSON を jq で処理するのが単純そうです。

VCSA の OVF を xq に渡し、JSON に変換してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
> xq . VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf | head -20
{
  "ovf:Envelope": {
    "@xmlns:ovf": "http://schemas.dmtf.org/ovf/envelope/1",
    "@xmlns": "http://schemas.dmtf.org/ovf/envelope/1",
    "@xmlns:rasd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
    "@xmlns:vssd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData",
    "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
    "@xmlns:vmw": "http://www.vmware.com/schema/ovf",
    "References": {
      "ovf:File": [
        {
          "@ovf:href": "layout.json",
          "@ovf:size": "15154",
          "@ovf:id": "layout.json_id"
        },
        {
          "@ovf:href": "VMware-vCenter-Server-Appliance-7.0.3.00300-19234570-upgrade-requirements.rpm",
          "@ovf:size": "91056805",
          "@ovf:id": "VMware-vCenter-Server-Appliance-7.0.3.00300-19234570-upgrade-requirements.rpm_id"
        }

XML タグの属性については、JSON で置き換える際に @属性名 と変換されていますが、その他は雰囲気が伝わるかと思います。

では、OVF パラメーターのキーをリストしてみます。キーは xpath で表すなら /ovf:Envelope/VirtualSystem/ProductSection/Property/@ovf:key になります。 jq では特殊文字は " (ダブルクォート) で囲むので、'."ovf:Envelope".VirtualSystem.ProductSection[].Property[]."@ovf:key"' となります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
> xq . VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf | jq '."ovf:Envelope".VirtualSystem.ProductSection[].Property[]."@ovf:key"'
"guestinfo.cis.appliance.net.addr.family"
"guestinfo.cis.appliance.net.mode"
"guestinfo.cis.appliance.net.addr"
"guestinfo.cis.appliance.net.prefix"
"guestinfo.cis.appliance.net.gateway"
"guestinfo.cis.appliance.net.dns.servers"
"guestinfo.cis.appliance.net.pnid"
"guestinfo.cis.appliance.net.ports"
"guestinfo.cis.vmdir.username"
"guestinfo.cis.vmdir.password"
"guestinfo.cis.vmdir.domain-name"
"guestinfo.cis.vmdir.site-name"
"guestinfo.cis.vmdir.first-instance"
"guestinfo.cis.vmdir.replication-partner-hostname"
"guestinfo.cis.db.type"
"guestinfo.cis.db.user"
"guestinfo.cis.db.password"

表にしてまとめておきたいので、jq の出力を加工します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> xq . VMware-vCenter-Server-Appliance-7.0.3.00300-19234570_OVF10.ovf | \
  jq -r '.["ovf:Envelope"].VirtualSystem.ProductSection[].Property[]' | \
  jq -r '.result = "|" + ."@ovf:key" + "|" + ."@ovf:userConfigurable" + "|" | .result'
|guestinfo.cis.appliance.net.addr.family|true|
|guestinfo.cis.appliance.net.mode|true|
|guestinfo.cis.appliance.net.addr|true|
|guestinfo.cis.appliance.net.prefix|true|
|guestinfo.cis.appliance.net.gateway|true|
|guestinfo.cis.appliance.net.dns.servers|true|
...

得られた出力を以下に表として記録します。結構 @ovf:userConfigurable=false のものが多いことがわかります。 VCSA のデプロイを ovftool のみで完全に自動化するには、guestinfo.cis.deployment.autoconfig を使うことになります。

parameter boolean
guestinfo.cis.appliance.net.addr.family true
guestinfo.cis.appliance.net.mode true
guestinfo.cis.appliance.net.addr true
guestinfo.cis.appliance.net.prefix true
guestinfo.cis.appliance.net.gateway true
guestinfo.cis.appliance.net.dns.servers true
guestinfo.cis.appliance.net.pnid true
guestinfo.cis.appliance.net.ports false
guestinfo.cis.vmdir.username false
guestinfo.cis.vmdir.password true
guestinfo.cis.vmdir.domain-name false
guestinfo.cis.vmdir.site-name false
guestinfo.cis.vmdir.first-instance false
guestinfo.cis.vmdir.replication-partner-hostname false
guestinfo.cis.db.type false
guestinfo.cis.db.user false
guestinfo.cis.db.password false
guestinfo.cis.db.servername false
guestinfo.cis.db.serverport false
guestinfo.cis.db.provider false
guestinfo.cis.db.instance false
guestinfo.cis.appliance.root.passwd true
guestinfo.cis.appliance.root.shell false
guestinfo.cis.appliance.ssh.enabled false
guestinfo.cis.appliance.time.tools-sync false
guestinfo.cis.appliance.ntp.servers false
guestinfo.cis.deployment.node.type false
guestinfo.cis.system.vm0.hostname false
guestinfo.cis.system.vm0.port false
guestinfo.cis.upgrade.source.vpxd.ip false
guestinfo.cis.upgrade.source.ma.port false
guestinfo.cis.upgrade.source.vpxd.user false
guestinfo.cis.upgrade.source.vpxd.password false
guestinfo.cis.upgrade.source.guest.user false
guestinfo.cis.upgrade.source.guest.password false
guestinfo.cis.upgrade.source.guestops.host.addr false
guestinfo.cis.upgrade.source.guestops.host.user false
guestinfo.cis.upgrade.source.guestops.host.password false
guestinfo.cis.upgrade.source.ssl.thumbprint false
guestinfo.cis.upgrade.source.platform false
guestinfo.cis.upgrade.source.export.directory false
guestinfo.cis.upgrade.import.directory false
guestinfo.cis.upgrade.user.options false
guestinfo.cis.ad.domain-name false
guestinfo.cis.ad.domain.username false
guestinfo.cis.ad.domain.password false
guestinfo.cis.vpxd.ha.management.addr true
guestinfo.cis.vpxd.ha.management.port true
guestinfo.cis.vpxd.ha.management.user true
guestinfo.cis.vpxd.ha.management.password true
guestinfo.cis.vpxd.ha.management.thumbprint true
guestinfo.cis.vpxd.ha.placement true
guestinfo.cis.netdump.enabled false
guestinfo.cis.silentinstall false
guestinfo.cis.lookup_timeout false
guestinfo.cis.clientlocale false
guestinfo.cis.feature.states false
guestinfo.cis.ceip_enabled true
guestinfo.cis.deployment.autoconfig false
guestinfo.cis.vpxd.mac-allocation-scheme.prefix false
guestinfo.cis.vpxd.mac-allocation-scheme.prefix-length false
guestinfo.cis.vpxd.mac-allocation-scheme.ranges false
guestinfo.cis.plugin-isolation.hostname false
guestinfo.cis.hadcs.enabled true
guestinfo.cis.fips.enabled false
guestinfo.cis.desired.state false
domain true
searchpath true
comments powered by Disqus