ラベル rbvmomi の投稿を表示しています。 すべての投稿を表示
ラベル rbvmomi の投稿を表示しています。 すべての投稿を表示

2020年4月1日水曜日

rbvmomi を使って CloneVM_Task 時に VM をカスタマイズする方法

概要

VM をクローンするときにカスタマイズ機能を使うと VM 内のホスト名や IP の固定化ができます
今回は rbvmomi を使って CloneVM_Task でホスト名をカスタマイズする方法を紹介します

環境

  • vSphere vCenter 6.5.0 9451637
  • CentOS 7.7.1908
  • Ruby 2.5.5p157
  • rbvmomi 2.3.1

とりあえず CloneVM_Task してみる

まずは普通に CloneVM_Task する方法を確認してみます
ちなみに今回、クローン元の VM は Ubuntu16.04 を使用しています

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.10',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('dc') || fail('datacenter not found')
vm = dc.find_vm('u16') || fail('VM not found')

# Create config spec and relocate spec
config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
  :annotation => 'vm clone'
)
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec

# Create clone spec
spec = RbVmomi::VIM.VirtualMachineCloneSpec(
  :config => config_spec,
  :location => relocate_spec,
  :powerOn => false,
  :template => false
)

# Start clone vm
vm.CloneVM_Task(
  :folder => vm.parent,
  :name => 'clone',
  :spec => spec
).wait_for_completion

ポイント

CloneVM_Task する際には VirtualMachineCloneSpec というパラメータが必要になります
また VirtualMachineCloneSpec には更にいろいろな spec を設定することができそれによりクローンする VM のネットワークやストレージの構成、サマリ情報などを変更することができます
今回はとりあえず同じスペックの VM で名前が異なる VM をクローンします
クローン先は vm.parent を指定しておりこうすることでクローン元と同じディレクトリにクローンすることができます

カスタマイズを利用して CloneVM_Task 時に hostname を変更する

次に本題であるカスタマイズ機能を作ってみたいと思います

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.10',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('dc') || fail('datacenter not found')
vm = dc.find_vm('u16') || fail('VM not found')

config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
  :annotation => 'vm clone'
)
# Create nic spec
nic_setting = RbVmomi::VIM.CustomizationAdapterMapping(
  :adapter => RbVmomi::VIM.CustomizationIPSettings(
    :ip => RbVmomi::VIM.CustomizationDhcpIpGenerator
  )
)
# Create LinuxPrep
linux_prep = RbVmomi::VIM.CustomizationLinuxPrep(
  :domain => 'localdomain',
  :hostName => RbVmomi::VIM.CustomizationFixedName(
    :name => 'clone'
  )
)
# Create ip setting with nic spec and LinuxPrep
ip_settings = RbVmomi::VIM.CustomizationGlobalIPSettings
customization_spec = RbVmomi::VIM.CustomizationSpec(
  :globalIPSettings => ip_settings,
  :identity => linux_prep,
  :nicSettingMap => [nic_setting]
)
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec

# Create clone spec customizing mode
spec = RbVmomi::VIM.VirtualMachineCloneSpec(
  :config => config_spec,
  :customization => customization_spec,
  :location => relocate_spec,
  :powerOn => true,
  :template => false
)

# Start clone
vm.CloneVM_Task(
  :folder => vm.parent,
  :name => 'clone',
  :spec => spec
).wait_for_completion

ポイント

VirtualMachineCloneSpec に先程設定しなかった :customization を設定します
:customization には CustomizationSpec を設定します
そして CustomizationSpec にホスト名とネットワークをカスタマイズするためのスペックを設定します
今回はネットワークに関しては特に何も設定しないので情報は CustomizationGlobalIPSettingsCustomizationAdapterMapping はとりあえず作っているだけという感じです
ホスト名を変更するためには CustomizationLinuxPrep を使います
:hostName のフィールドに CustomizationFixedName を使うことで VM 内のホスト名を変更することができます

動作確認

実行して VM が起動するとホスト名の部分が指定したものに変更されているのが確認できると思います
実際にコンソールや ssh で確認してもホスト名が変更されているのが確認できると思います

最後に

rbvmomi を使って CloneVM_Task のカスタマイズ機能を使ってみました
Hashicorp 社の terraform で使われている terraform-provider-vsphere などでこの機能は使われいるようです
また VM のカスタマイズは実際は perl スクリプト (Customize.pl ?) が実行されているようです
カスタマイズ後に削除されてしまうようですが中身がどんなものか実際に見てみていものです

参考サイト

2020年3月11日水曜日

rbvmomi で指定のデータセンター配下にあるホストの一覧をフラットに取得する方法

概要

タイトルの通りです
ホストはフォルダやクラスタに属しているのでそれを考慮して再帰処理ですべてのホスト情報をフラットに取得します

環境

  • Ruby 2.5.5p157
  • rbvmomi 2.3.0

サンプルコード

require 'rbvmomi'

def search_hosts(host_folder)
  ret = []
  if host_folder.is_a? RbVmomi::VIM::Folder
    host_folder.children.each { |target|
      if host_folder.is_a? RbVmomi::VIM::HostSystem
        # host
        ret.append(target)
      else
        # folder or cluster
        ret.concat(search_hosts(target))
      end
    }
  else
    # RbVmomi::VIM::ClusterComputeResource
    ret.concat(host_folder.host)
  end
  ret
end

# main
vim = RbVmomi::VIM.connect(
  host: '192.168.100.10',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true'
)
dc_name = 'Datacenter'
dc = vim.serviceInstance.find_datacenter(dc_name) || fail('datacenter not found')
dc.hostFolder.children.each { |host_folder|
  hosts = search_hosts(host_folder)
  hosts.each { |host|
    puts host.name
  }
}

解説

メインの処理でデータセンターの取得とデータセンター配下にあるフォルダ or クラスタ情報を取得します
おそらくですがデータセンターの直下にホストを追加することはできないはずです

取得したフォルダ or クラスタを再帰的に検索してホストを取得します
フォルダの配下にはホスト or クラスタが配置することができるようです
またクラスタ配下にはホストしか配置できないようです
それらを考慮して search_hosts の再帰処理を実装します
フォルダの場合のみ再度 search_hosts を実行します
search_hosts は最終的にホストの一覧の配列を返却します

とりあえずこれで動作しますが改良すれば RbVmomi::VIM::Datacenter クラスのインスタンスメソッドとしても定義できそうです
クラスオープンなどを使って追加しても良いかなと思います

最後に

rbvmomi でデータセンター配下にあるホストをすべて取得してみました
もしかすると直接取得できる API が生えているかもしれませんが見つからなかったのと探すのが面倒だったので自分で作成しました

2019年3月19日火曜日

rbvmomi で QueryChangedDiskAreas をコールしてみた

概要

過去に golang で QueryChangedDiskAreas をコールしました
今回は rbvmomi からコールしてみました

環境

  • CentOS 7.5.1840
  • Ruby 2.5.0p0
  • rbvmomi 2.0.1

サンプルコード

事前に changeTrackingEnabled は有効にしておきましょう
また snapshot も作成しておきます
vCenter などの接続情報は適宜変更してください

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxx',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('dc') || fail('datacenter not found')
vm = dc.find_vm('vm') || fail('VM not found')

params = {
  snapshot: RbVmomi::VIM.VirtualMachineSnapshot(vim, "snapshot-100"),
  deviceKey: 2000,
  startOffset: 0,
  changeId: "*"
}
ret = vm.QueryChangedDiskAreas(params)
puts ret.startOffset
puts ret.length
ret.changedArea.each { |area|
  puts "start => #{area.start}, length => #{area.length}"
}

少し解説

params の 4 つの項目はすべて必須です
スナップショットの moref と HDD のデバイス番号、探索を開始するオフセット、そしてスナップショットと差分を取得する開始地点の ID です
すべて mob から取得できます

QueryChangedDiskAreas の戻り値は RbVmomi::VIM::DiskChangeInfo になります
アクセス可能な属性は startOffset, length, changedArea になります
さらに changedAreaRbVmomi::VIM::DiskChangeExtent になっておりこれをループすることで実際にディスクの差分箇所を確認することができます

最後に

rbvmomi で QueryChangedDiskAreas を実行してみました
今回の手順の場合は事前に CBT とスナップショットを取得しておく必要があります
QueryChangedDiskAreas に渡らすパラメータの構造が少しわかりにくいくらいであとは簡単にコールできると思います

参考サイト

2018年12月21日金曜日

InstantClone_Task 時に guestinfo を渡す方法

概要

前回 InstantClone をサクっと試してみました
実は InstantClone_Task 実行時に作成されたインスタントクローン VM に guestinfo を渡すことができます
gusetinfo は簡単に言えば GuestOS (例えば Ubuntu など) 上で参照することができる変数です

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)
  • Ruby 2.5.0p0
  • rbvmomi (2.0.0)

guestinfo を指定する方法

spec に config を key/value 形式で並べるだけです

<spec>
   <name>ic01</name>
   <location></location>
   <!-- ok -->
   <config>
      <key>guestinfo.ic.hoge</key>
      <value xsi:type="xsd:string">guestinfo.ic.hoge</value>
   </config>
   <config>
      <key>guestinfo.ic.fuga</key>
      <value xsi:type="xsd:string">guestinfo.ic.fuga</value>
   </config>
   <config>
      <key>guestinfo.iic.fuga</key>
      <value xsi:type="xsd:string">guestinfo.iic.fuga</value>
   </config>
   <config>
      <key>guestinfo.fuga</key>
      <value xsi:type="xsd:string">guestinfo.fuga</value>
   </config>
   <!-- ng -->
   <config>
      <key>ic.fuga</key>
      <value xsi:type="xsd:string">ic.fuga</value>
   </config>
   <config>
      <key>fuga</key>
      <value xsi:type="xsd:string">fuga</value>
   </config>
</spec>

少しポイントがあります
渡せる key に制約があるようで必ず guestinfo で始まっている必要がありました
もし制約違反していると値が GuestOS 側に渡りません
参照しても Invalid key name supplied になります
また、存在しない key を指定すると No value found になります

どうやって参照するの

vmware-tools の機能を使います
vmware-tools がインストールされていると vmware-rpctool コマンドが使えます
実際にインスタントクローンされた VM にログインして以下のコマンドを叩いてみましょう

  • vmware-rpctool "info-get guestinfo.ic.hoge"

すると設定した value 側の値が取得できると思います

おまけ: rbvmomi でやってみた

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('Datacenter') || fail('datacenter not found')
vm = dc.find_vm('u18_01') || fail('VM not found')

spec = {
  name: "ic01", location: "",
  config: [
    {key: "guestinfo.ic.hoge", value: "hoge"},
    {key: "guestinfo.ic.fuga", value: "fuga"}
  ]
}
ret = vm.InstantClone_Task(spec: spec).wait_for_completion
puts ret

spec の指定を mob で実行する spec に合わせるだけです

最後に

InstantClone_Task 時に gusetinfo を渡す方法を紹介しました
実はこれを応用すると前回紹介した InstantClone 時に IP アドレスがバッティングしてしまう問題を解決することができます

要するにネットワーク情報を guestinfo で渡してインスタントクローン VM が作成されたときにネットワークを設定するスクリプトを実行することでバッティングを解消するという流れになります

ちょっと力技感も強いですがこちらで紹介されていたのでほぼオフィシャルなのかなと思います
具体的なやり方も分かったら紹介したいと思います

参考サイト

2018年12月20日木曜日

vSphere 6.7 で InstantClone_Task を試してみた

概要

vSphere 6.7 から正式に InstantClone が使えるようになったので試してみました
Web Client からは使えないので mob と SOAP API (rbvmomi) から実行してみました

環境

  • VCSA 6.7.0.20000 build 10244745
  • ESXi 6.7.0 Update 1 (Build 10302608)
  • Ruby 2.5.0p0
  • rbvmomi (2.0.0)

mob から実行する

対象の VM に移動して methods 内から InstantClone_Task を選択すれば OK です
条件としては対象の VM が起動している必要があります (停止中の VM に対して実行したところエラーになりました)
instant_clone1.png

最低限必要な spec は以下の通りです

<spec>
   <name>ic01</name>
   <location>
   </location>
</spec>

instant_clone2.png

これで Invoke すれば InstantClone で VM が作られます
location を指定すれあ別のホストに作成することもできます

rbvmomi から実行する

プログラムからも実行してみました

  • bundle init
  • vim Gemfile
gem "rbvmomi"
  • bundle install --path vendor
  • vim main.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.30',
  user: 'administrator@vsphere.local',
  password: 'xxxxxxxxxxx',
  insecure: 'true',
  rev: '6.7.1'
)

dc = vim.serviceInstance.find_datacenter('Datacenter') || fail('datacenter not found')
vm = dc.find_vm('vm01') || fail('VM not found')

spec = {name: "ic01", location: ""}
ret = vm.InstantClone_Task(spec: spec).wait_for_completion
puts ret
  • bundle exec ruby main.rb

これだけです
rbvmomi で実施する場合は VIM.connect する際に rev: '6.7.1' の指定が必要です
WSDL から InstantClone_Task を探すのですが rev: を明示的に指定しないと探すことができずエラーになってしまいます
またパワーオンになっていないと以下のエラーが発生します
InvalidPowerState: The attempted operation cannot be performed in the current state (Powered off). (RbVmomi::Fault) (edited)

ちなみに WSDL の URL は https://192.168.100.30/sdk/vim.wsdl という感じの URL で確認できます

最後に

InstanceClone_Task を試してみました
いろいろ経緯とかを調べてみるとコンテナの需要が高まったのと CI/CD で使えないかという要望があったららしいです
なのであくまでも用途としては使い捨て VM という感じで使うのだと思います

一つ気になったのはインスタンスクローン後にクローン元の VM と同じ IP が振られる現象が多々ありました
ネットワークのインタフェースの MAC アドレスクローン直後は同一に見えてしまうため DHCP も同じ IP を払い出しているんだと思います
再起動したり一定時間経過すると異なる IP が振られたのですが、直後はどう対処すれば良いか不明でした

P.S どうやら GuestInfo の仕組みを使って VM ないで IP のリフレッシュ作業を行う必要があるようです
https://www.virtuallyghetto.com/2018/04/new-instant-clone-architecture-in-vsphere-6-7-part-2.html
おそらくこの方法が確実だと思われます

参考サイト

2018年3月21日水曜日

rbVmomi で vMotion する方法 (RelocateVM_Task 編)

概要

rbVmomi を使って VM の vMotion をしてみました
今回は RelocateVM_Task を使って vMotion しています

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3
  • vCenter 6.5

コード

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

host = nil
priority = :defaultPriority
dc.hostFolder.children.each { |cluster|
  if cluster.is_a? RbVmomi::VIM::ClusterComputeResource
    host = cluster.host.select { |host|
      host.name == 'target_hostname'
    }.first
  end
}

migrate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(host: host)
vm.RelocateVM_Task(spec: migrate_spec, priority: priority).wait_for_completion

RelocateVM_Task の場合 VirtualMachineRelocateSpec を引数に取ります
また今回は host のみを指定しているため単純なホスト間の vMotion になりますがストレージ情報も指定できます
ストレージ情報を指定すると Storage vMotion にすることができます

host の情報は MigrateVM_Task 同様 HostSystem の情報を指定する必要があります

最後に

RelocateVM_Task を使って rbVmomi から vMotion してみました
vSphere 6.5 からはこちらを使うことが推奨されています

2018年3月20日火曜日

rbVmomi で vMotion する方法 (MigrateVM_Task 編)

概要

rbVmomi を使って VM の vMotion をしてみました
今回は MigrateVM_Task を使って vMotion しています

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3
  • vCenter 6.5

コード

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

host = nil
priority = :defaultPriority
dc.hostFolder.children.each { |cluster|
  if cluster.is_a? RbVmomi::VIM::ClusterComputeResource
    host = cluster.host.select { |host|
      host.name == 'target_hostname'
    }.first
  end
}
vm.MigrateVM_Task(host: host, priority: priority).wait_for_completion

host と priority を指定しています
host は文字列をただ指定するだけではだめで HostSystem クラスのオブジェクトを設定する必要があります

今回はクラスタ直下にあるホストを取得していますが環境によってはフォルダを挟んでいる環境もあると思うので検索ロジックの部分は必要に併せて変更してください

最後に

MigrateVM_Task を使って rbVmomi で vMotion してみました
ちなみに MigrateVM_Task は vCenter 6.5 では deprecated になっているので RelocateVM_Task を使うようにしましょう

2017年11月9日木曜日

rbvmomi で GuestFileManager を使って VM 内のファイルやディレクトリを操作してみる

概要

前回 GuestProcessManager を使って VM 内のプロセス制御をしてみました
同じようにファイルやディレクトリを制御できる GuestFileManager という機能があるので今回はこれを使ってみました
ProcessManager に比べて操作できる機能が多いです

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3

ファイルの一覧を表示する

  • vim list_files.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/"
files = vim.serviceContent.guestOperationsManager.fileManager.ListFilesInGuest(
  vm: vm,
  auth: auth,
  filePath: path,
  # index: 0,
  # maxResults: 50,
  matchPattern: "^hoge$"
)

files.props[:files].each { |file|
  puts file.props[:path]
  puts file.props[:type]
  puts file.props[:size]
  puts file.props[:attributes].props[:modificationTime]
  puts file.props[:attributes].props[:accessTime]
  puts file.props[:attributes].props[:ownerId]
  puts file.props[:attributes].props[:groupId]
  puts file.props[:attributes].props[:permissions].to_s(8)
}

ListFilesInGuest を使います
パラメータに path, index, maxResults, matchPattern を指定できます
path はファイルでもいいですしディレクトリでも OK です
ファイルの場合は 1 つだけ取得しディレクトリの場合は指定したディレクトリ配下のファイル群を取得します
index はページネート用のインデックスです
デフォルトは 0 ページ目からの取得になります
maxResults は 1 ページで取得する最大の件数になります
デフォルトは 50 です
例えば取得件数が 100 ファイルある場合に 51 件目以降を取得する場合は index を 1 にしてそこから 50 件取得するか index を 0 のままで maxResults を 100 にするかになります

matchPattern は正規表現に合致したファイル群を取得するパラメータです
上記のサンプルのように指定した場合は hoge にマッチするファイルだけを取得します
これを ^hoge というふうにすると先頭が hoge というファイルにマッチするので hoge2 や hogefuga というファイルも検索結果に含まれます
使用できる正規表現は Perl の正規表現になります

検索結果は GuestListFileInfo というクラスのオブジェクトが返ってきます
この中のプロパティ :files にというものがあり、その中に各ファイルの情報を管理する GuestFileInfo という配列があります
あとは each とハッシュを使って必要な情報を取得するだけですがその中にファイルの権限を取得できる permissions があります
これがデフォルトだと 10 進数表記になっているのでわかりやすいように 8 進数表記に変換してあげると良いと思います

ファイルの属性情報を変更する

  • vim change_file_attrs.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/hoge"
attr = RbVmomi::VIM::GuestPosixFileAttributes(
  # groupId: 0
  # ownerId: 0
  permissions: 33279 # 100777
)

vim.serviceContent.guestOperationsManager.fileManager.ChangeFileAttributesInGuest(
  vm: vm,
  auth: auth,
  guestFilePath: path,
  fileAttributes: attr
)

ChangeFileAttributesInGuest を使います
guestFilePath で指定したファイルの属性情報を変更します
今回 Ubuntu VM 内のファイルの属性情報を変更するので GuestPosixFileAttributes を使用します
パラメータにはファイルの管理者情報とパーミッションを指定できます
上記のサンプルでは権限を変更しています
権限の指定方法は 10 進数で指定する必要があるので注意してください

返り値が void なので結果を確認したい場合は ListFilesInGuest を実行してファイルの内容を確認してください

テンポラリのディレクトリを作成する

  • vim create_tmp_dir.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp"

ret = vim.serviceContent.guestOperationsManager.fileManager.CreateTemporaryDirectoryInGuest(
  vm: vm,
  auth: auth,
  prefix: "prefix-",
  suffix: "-suffix",
  directoryPath: path
)

puts ret

CreateTemporaryDirectoryInGuest を使います
パラメータにはディレクトリを作成するパス (directoryPath) と作成するディレクトリの prefix と suffix をオプションで指定することができます
作成されるディレクトリ名は vmware000 という形式で作成されます
000 の部分はランダムの数字が入ります
その前後に prefix と suffix で指定した文字列が付与されます

返り値は作成されたディレクトリのフルパス情報が String で返却されます

テンポラリのファイルを作成する

  • vim create_tmp_file.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp"

ret = vim.serviceContent.guestOperationsManager.fileManager.CreateTemporaryFileInGuest(
  vm: vm,
  auth: auth,
  prefix: "prefix-",
  suffix: "-suffix",
  directoryPath: path
)

puts ret

CreateTemporaryFileInGuest を使います
挙動やパラメータは前述の CreateTemporaryDirectoryInGuest とほぼ同じです
ディレクトリを作成するかファイルを作成するかの違いになります
ンポラリファイルの権限は 600 で作成されるようです

ディレクトリの削除をする

  • vim delete_dir.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/prefix-vmware130-suffix"

vim.serviceContent.guestOperationsManager.fileManager.DeleteDirectoryInGuest(
  vm: vm,
  auth: auth,
  directoryPath: path,
  recursive: true
)

DeleteDirectoryInGuest を使います
パラメータは削除対象のディレクトリと再帰的に削除するかのフラグを渡します
recursive が false の場合に削除対象のディレクトリ配下に更にディレクトリがあるとエラーとなります

返り値は void になります

ファイルの削除をする

  • vim delete_file.rb
require 'rbvmomi'

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/prefix-vmware136-suffix"

vim.serviceContent.guestOperationsManager.fileManager.DeleteFileInGuest(
  vm: vm,
  auth: auth,
  filePath: path
)

DeleteFileInGuest を使います
先程はディレクトリを削除しましたがこれはファイルを削除します
ディレクトリを指定した場合やファイルが存在しない場合にはエラーとなります
返り値は void です

ディレクトリを作成する

  • vim make_dir.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/fuga/hoge"

vim.serviceContent.guestOperationsManager.fileManager.MakeDirectoryInGuest(
  vm: vm,
  auth: auth,
  directoryPath: path,
  createParentDirectories: true
)

MakeDirectoryInGuest を使います
CreateTemporaryDirectoryInGuest とほぼ同じですがこちらの場合は作成するディレクトリ名に規則はありません
createParentDirectories は mkdir でいうところの -p オプションで上位のディレクトリがなかった場合に作成してくれます

ただ CreateTemporaryDirectoryInGuest とは違いなぜか返り値は void になっています
そして MakeFileInGuest はありません

ディレクトリを移動する

  • vim move_dir.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

src = "/tmp/fuga/hoge2"
dst = "/tmp/fuga/hoge"

vim.serviceContent.guestOperationsManager.fileManager.MoveDirectoryInGuest(
  vm: vm,
  auth: auth,
  srcDirectoryPath: src,
  dstDirectoryPath: dst
)

MoveDirectoryInGuest を使います
src にあるディレクトリを dst に移動します
dst 側にすでにディレクトリがある場合はエラーになります

返り値は void になります

ファイルを移動する

  • vim move_file.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

src = "/tmp/fuga/file1"
dst = "/tmp/fuga/file2"

vim.serviceContent.guestOperationsManager.fileManager.MoveFileInGuest(
  vm: vm,
  auth: auth,
  srcFilePath: src,
  dstFilePath: dst,
  overwrite: false
)

MoveFileInGuest を使います
移動前と移動後のファイルパスを指定します
移動前のファイルが存在しない場合はエラーになります
また移動後のファイル名がすでに存在する場合に上書きするオプションがあります
true で上書きします、false を指定した場合に移動後のファイル名がすでにある場合はエラーとなります

ホストから ESXi にファイルを転送する

  • vim transfer_from.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/fuga/file1"

ret = vim.serviceContent.guestOperationsManager.fileManager.InitiateFileTransferFromGuest(
  vm: vm,
  auth: auth,
  guestFilePath: path
)

puts ret.props[:url]

InitiateFileTransferFromGuest を使います
これは VM 上のファイルをから ESXi 上にアップロードすることができる機能になります

返り値は RbVmomi::VIM::FileTransferInformation になります
この中に url というフィールドが含まれておりこの URL にアクセスすると ESXi 上にアップロードされたファイルを確認することができます
URL は一度しかアクセスすることができません

ESXi からホストにファイルを転送する

  • vim transfer_to.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

path = "/tmp/fuga/file1"
attr = RbVmomi::VIM::GuestPosixFileAttributes(
  # groupId: 0
  # ownerId: 0
  permissions: 33188 # 100644
)

ret = vim.serviceContent.guestOperationsManager.fileManager.InitiateFileTransferToGuest(
  vm: vm,
  auth: auth,
  guestFilePath: path,
  fileAttributes: attr,
  fileSize: 26,
  overwrite: true
)

puts ret

InitiateFileTransferToGuest を使います
ローカルにあるファイルにアクセスするために ESXi 経由で取得できるURL を取得することができます
URL には一度しかアクセスできません

最後に

GuestFileManager を使って VM 内のファイルやディレクトリの操作をしてみました
GuesetProcessManager に比べいろいろな処理ができる印象です
ただ、相変わらず返り値は void が多いので確認には工夫が必要です

今回も認証はユーザ名/パスワードにしました
SSPI or Ticketed でも頑張れば認証する方法があるかもしれません

2017年11月7日火曜日

rbvmomi で GuestProcessManager を使って VM 内のプロセスを操作してみる

概要

GuestProcessManager は VM のプロセスを制御することができる vSphere API です
VM に SSH ログインすることなくコマンドなどを実行することができます
今回は Ruby + rbvmomi を使ってプロセスの制御を行ってみました

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3

ライブラリインストール

  • bundle init
  • vim Gemfile
gem "rbvmomi"
  • bundle install --path vendor

プロセスの一覧を表示する

  • vim list_processes.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

processes = vim.serviceContent.guestOperationsManager.processManager.ListProcessesInGuest(
  vm: vm,
  auth: auth,
  pids: []
)

processes.each { |process|
  puts "name => #{process.props[:name]}, pid => #{process.props[:pid]}, owner => #{process.props[:owner]}, cmdLine => #{process.props[:cmdLine]}, startTime => #{process.props[:startTime]}"
}

ListProcessesInGuest を使います
引数には VM の情報と VM の認証情報を付与します
第 3 引数に pid の配列を指定することができ、指定のプロセス情報だけ取得することもできます
結果は RbVmomi::VIM::GuestProcessInfo の配列が返ってくるので each で回してプロパティにアクセスしています

VM の情報を取得するのに find_datacenter -> find_vm という流れを使っています
データセンターを取得したあとそのデータセンター配下にある VM を検索するのですが、フォルダで階層化されている場合はパス形式で指定する必要があるので注意してください

VM 情報取得 -> VM 認証情報作成の流れはこのあとの処理でも共通処理として登場します

環境変数の一覧を表示する

  • vim read_environment.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

env = vim.serviceContent.guestOperationsManager.processManager.ReadEnvironmentVariableInGuest(
  vm: vm,
  auth: auth,
  names: []
)

puts env

ReadEnvironmentVariableInGuest を使います
流れは先程のプロセスを取得するスクリプトとほぼ同じです
結果が String の配列で返ってくる部分が違います
第 3 引数に環境変数名を配列で指定すれば指定した環境変数のみ取得することも可能です

プロセスを起動する

  • vim start_program.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

spec = RbVmomi::VIM::GuestProgramSpec(
  programPath: "/bin/hostname",
  arguments: ""
)

pid = vim.serviceContent.guestOperationsManager.processManager.StartProgramInGuest(
  vm: vm,
  auth: auth,
  spec: spec
)

puts pid

StartProgramInGuest を使います
第 3 引数に実行するコマンド情報を指定する必要があります
コマンド情報は GuestProgramSpec から生成します
コマンド情報はフルパスで指定しましょう、PATH が通っているコマンドでもフルパスで指定しないと見つかりません

返り値は実行したコマンドのプロセス ID が返却されます
ここが少し残念なのですが実行したコマンドの標準出力などを取得することができません
なので結果がほしい場合はどこかのファイルのリダイレクトしてあとからそのファイルを取得するなどの工夫が必要になります

プロセスを停止する

  • vim terminate_process.rb
require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

auth = RbVmomi::VIM::NamePasswordAuthentication(
  interactiveSession: false,
  username: "root",
  password: "password"
)

pid = 3004
vim.serviceContent.guestOperationsManager.processManager.TerminateProcessInGuest(
  vm: vm,
  auth: auth,
  pid: pid
)

TerminateProcessInGuest を使います
第 3 引数には停止するプロセス ID を指定します
これも少し残念なのですが、返り値は void です
成功したかどうかの判断ができないので、もし判断したい場合はこのあとで ListProcessesInGuest などを呼び出してプロセスがなくなっているか確認するなどの工夫が必要になります

最後に

GuestProcessManager を使って vmware-tools 経由で VM 内のプロセスの制御をしてみました
VM に SSH する必要がないのが嬉しい点かなと思います
簡単なプロセス制御であれば GuestProcessManager で代用できるかなと思います
また実行条件としては vmware-tools が起動している必要があります

今回認証は VM のユーザ、パスワード認証を使いました
そもそもパスワード認証を設定していない場合もあるのでその場合は SSPIAuthentication や TicketedSessionAuthentication を使うのですがどうやらこれらだと OperationNotSupportedByGuest になってしまいアクセスできません
やり方が悪いのかもわからないのですが、ネット上でも同じように SSPI or Ticket 認証で GuestProcessManager を制御しようとしているようなのですが解決できてそうな情報は見つかりませんでした
(もし判明したら教えてほしいくらいです)

あと rbvmomi の場合ドットつなぎでオブジェクトを辿り API をコールします
次に何をつなぐかわからなくなるケースが多くなると思うのでその場合は vCenter の mob を見ながらやると次に何のメソッドがコールできるかわかると思います
本当はそ rbvmomi のコードを見ながらやりたいのですが、API の殆どが vmodl.db というバイナリファイルで定義されているため内容が確認するのが大変です

参考サイト