Top Level Namespace

Defined Under Namespace

Modules: RVC Classes: ProgressStream

Constant Summary collapse

URI_REGEX =
%r{
  ^
  (?:
    ([^@:]+)
    (?::
     ([^@]*)
    )?
    @
  )?
  ([^@:]+)
  (?::(.*))?
  $
}x
VNC =

Copyright © 2011 VMware, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

ENV['VNC'] || search_path('vinagre') || search_path('tightvnc') || search_path('vncviewer')
VMRC_BASENAME =
"#{VMRC_NAME}.xpi"
VMRC_URL =
"http://cloud.github.com/downloads/vmware/rvc/#{VMRC_BASENAME}"
HELP_ORDER =
%w(basic vm)

Instance Method Summary collapse

Instance Method Details

#_add_device(vm, dev) ⇒ Object



563
564
565
566
567
568
569
570
# File 'lib/rvc/modules/vm.rb', line 563

def _add_device vm, dev
  spec = {
    :deviceChange => [
      { :operation => :add, :device => dev },
    ]
  }
  vm.ReconfigVM_Task(:spec => spec).wait_for_completion
end

#_add_net_device(vm, klass, network) ⇒ Object



572
573
574
575
576
577
578
579
580
581
582
583
584
# File 'lib/rvc/modules/vm.rb', line 572

def _add_net_device vm, klass, network
  _add_device vm, klass.new(
    :key => -1,
    :deviceInfo => {
      :summary => network,
      :label => `uuidgen`.chomp
    },
    :backing => VIM.VirtualEthernetCardNetworkBackingInfo(
      :deviceName => network
    ),
    :addressType => 'generated'
  )
end

#_extraConfig(vm, *regexes) ⇒ Object



450
451
452
453
454
455
456
457
# File 'lib/rvc/modules/vm.rb', line 450

def _extraConfig vm, *regexes
  vm.config.extraConfig.each do |h|
    if regexes.empty? or regexes.any? { |r| h[:key] =~ r }
      puts "#{h[:key]}: #{h[:value]}"
    end
  end
  nil
end

#_setExtraConfig(vm, hash) ⇒ Object



443
444
445
446
447
448
# File 'lib/rvc/modules/vm.rb', line 443

def _setExtraConfig vm, hash
  cfg = {
    :extraConfig => hash.map { |k,v| { :key => k, :value => v } },
  }
  vm.ReconfigVM_Task(:spec => cfg).wait_for_completion
end

#add_host(cluster, hostname, opts) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rvc/modules/cluster.rb', line 40

def add_host cluster, hostname, opts
  sslThumbprint = nil
  while true
    spec = {
      :force => false,
      :hostName => hostname,
      :userName => opts[:username],
      :password => opts[:password],
      :sslThumbprint => sslThumbprint,
    }
    task = cluster.AddHost_Task :spec => spec,
                                :asConnected => false
    begin
      task.wait_for_completion
      break
    rescue VIM::SSLVerifyFault
      puts "SSL thumbprint: #{$!.fault.thumbprint}"
      $stdout.write "Accept this thumbprint? (y/n) "
      $stdout.flush
      answer = $stdin.readline.chomp
      err "Aborted" unless answer == 'y' or answer == 'yes'
      sslThumbprint = $!.fault.thumbprint
    end
  end
end

#add_iscsi_target(hosts, opts) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/rvc/modules/host.rb', line 130

def add_iscsi_target hosts, opts
  hosts.each do |host|
    puts "configuring host #{host.name}"
    storage = host.configManager.storageSystem
    storage.UpdateSoftwareInternetScsiEnabled(:enabled => true)
    adapter = storage.storageDeviceInfo.hostBusAdapter.grep(VIM::HostInternetScsiHba)[0]
    storage.AddInternetScsiStaticTargets(
      :iScsiHbaDevice => adapter.device,
      :targets => [ VIM::HostInternetScsiHbaStaticTarget(:address => opts[:address], :iScsiName => opts[:iqn]) ]
    )
    storage.RescanAllHba
  end
end

#add_net_device(vm, opts) ⇒ Object



552
553
554
555
556
557
558
559
560
# File 'lib/rvc/modules/vm.rb', line 552

def add_net_device vm, opts
  case opts[:type]
  when 'e1000'
    _add_net_device vm, VIM::VirtualE1000, opts[:network]
  when 'vmxnet3'
    _add_net_device vm, VIM::VirtualVmxnet3, opts[:network]
  else err "unknown device"
  end
end

#add_nfs_datastore(hosts, opts) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/rvc/modules/host.rb', line 151

def add_nfs_datastore hosts, opts
  hosts.each do |host|
    datastoreSystem, = host.collect 'configManager.datastoreSystem'
    spec = {
      :accessMode => 'readWrite',
      :localPath => opts[:name],
      :remoteHost => opts[:address],
      :remotePath => opts[:path]
    }
    datastoreSystem.CreateNasDatastore :spec => spec
  end
end

#add_privilege(name, privileges) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/rvc/modules/role.rb', line 93

def add_privilege name, privileges
  role = cur_auth_mgr.roleList.find { |x| x.name == name }
  err "no such role #{name.inspect}" unless role
  cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
                                       :newName => role.name,
                                       :privIds => (role.privilege | privileges)

end

#annotate(vm, str) ⇒ Object



682
683
684
# File 'lib/rvc/modules/vm.rb', line 682

def annotate vm, str
  vm.ReconfigVM_Task(:spec => { :annotation => str }).wait_for_completion
end

#answer(vm, str) ⇒ Object



336
337
338
339
340
341
342
# File 'lib/rvc/modules/vm.rb', line 336

def answer vm, str
  q = vm.runtime.question
  err "no question" unless q
  choice = q.choice.choiceInfo.find { |x| x.label == str }
  err("invalid answer") unless choice
  vm.AnswerVM :questionid => q.path, :answerChoice => choice.key
end

#bootconfig(vm, opts) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/rvc/modules/vm.rb', line 263

def bootconfig vm, opts

  if opts[:show]
    pp vm.config.bootOptions
    return
  end

  cur_delay        = vm.config.bootOptions.bootDelay
  cur_retrydelay   = vm.config.bootOptions.bootRetryDelay
  cur_retryenabled = vm.config.bootOptions.bootRetryEnabled

  if opts[:delay] and opts[:delay] != cur_delay
    new_delay = opts[:delay]
  else
    new_delay = cur_delay
  end

  if opts[:retrydelay] and opts[:retrydelay] != cur_retrydelay
    new_retrydelay = opts[:retrydelay]
    new_retryenabled = true
  else
    new_retrydelay = cur_retrydelay
  end

  if opts[:enablebootretry]
    new_retryenabled = true
  elsif opts[:disablebootretry]
    new_retryenabled = false
  else
    new_retryenabled = cur_retryenabled
  end

  spec = { :bootOptions => {
    :bootDelay => new_delay,
    :bootRetryDelay => new_retrydelay,
    :bootRetryEnabled => new_retryenabled,
    }
  }

  vm.ReconfigVM_Task(:spec => spec).wait_for_completion
end

#cd(obj) ⇒ Object



144
145
146
147
148
149
# File 'lib/rvc/modules/basic.rb', line 144

def cd obj
  $shell.fs.cd(obj)
  $shell.session.set_mark '', [find_ancestor(RbVmomi::VIM::Datacenter)].compact
  $shell.session.set_mark '@', [find_ancestor(RbVmomi::VIM)].compact
  $shell.delete_numeric_marks
end

#change_device_connectivity(vm, label, connected) ⇒ Object



733
734
735
736
737
738
739
740
741
742
743
# File 'lib/rvc/modules/vm.rb', line 733

def change_device_connectivity vm, label, connected
  dev = vm.config.hardware.device.find { |x| x.deviceInfo.label == label }
  err "no such device" unless dev
  dev.connectable.connected = connected
  spec = {
    :deviceChange => [
      { :operation => :edit, :device => dev },
    ]
  }
  vm.ReconfigVM_Task(:spec => spec).wait_for_completion
end

#check_known_hosts(host, peer_public_key) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rvc/modules/vim.rb', line 183

def check_known_hosts host, peer_public_key
  known_hosts = RVC::KnownHosts.new
  result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s

  if result == :not_found
    puts "The authenticity of host '#{host}' can't be established."
    puts "Public key fingerprint is #{arg}."
    err "Connection failed" unless agree("Are you sure you want to continue connecting (y/n)? ", true)
    puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
    known_hosts.add 'vim', host, peer_public_key.to_s
  elsif result == :mismatch
    err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
  elsif result == :ok
  else
    err "Unexpected result from known_hosts check"
  end
end

#clone(src, dst, opts) ⇒ Object



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/rvc/modules/vm.rb', line 630

def clone src, dst, opts
  folder, name = *dst
  diskMoveType = nil

  if opts[:linked]
    deltaize_disks src
    diskMoveType = :moveChildMostDiskBacking
  end

  task = src.CloneVM_Task(:folder => folder,
                          :name => name,
                          :spec => {
                            :location => {
                              :diskMoveType => diskMoveType,
                              :host => opts[:host],
                              :pool => opts[:pool],
                            },
                            :template => opts[:template],
                            :powerOn => opts[:powerOn],
                          })
  progress [task]
end

#connect(uri, opts) ⇒ Object



378
379
380
# File 'lib/rvc/modules/vm.rb', line 378

def connect vm, label
  change_device_connectivity vm, label, true
end

#create(name, parent, opts) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rvc/modules/vm.rb', line 143

def create dest, opts
  err "must specify resource pool (--pool)" unless opts[:pool]
  err "must specify datastore (--datastore)" unless opts[:datastore]
  err "memory must be a multiple of 4MB" unless opts[:memory] % 4 == 0
  vmFolder, name = *dest
  datastore_path = "[#{opts[:datastore].name}]"
  config = {
    :name => name,
    :guestId => 'otherGuest',
    :files => { :vmPathName => datastore_path },
    :numCPUs => opts[:cpucount],
    :memoryMB => opts[:memory],
    :deviceChange => [
      {
        :operation => :add,
        :device => VIM.VirtualLsiLogicController(
          :key => 1000,
          :busNumber => 0,
          :sharedBus => :noSharing
        )
      }, {
        :operation => :add,
        :fileOperation => :create,
        :device => VIM.VirtualDisk(
          :key => -1,
          :backing => VIM.VirtualDiskFlatVer2BackingInfo(
            :fileName => datastore_path,
            :diskMode => :persistent,
            :thinProvisioned => true
          ),
          :controllerKey => 1000,
          :unitNumber => 0,
          :capacityInKB => realdisksize( opts[:disksize] )
        )
      }, {
        :operation => :add,
        :device => VIM.VirtualCdrom(
          :key => -2,
          :connectable => {
            :allowGuestControl => true,
            :connected => true,
            :startConnected => true,
          },
          :backing => VIM.VirtualCdromIsoBackingInfo(
            :fileName => datastore_path
          ),
          :controllerKey => 200,
          :unitNumber => 0
        )
      }, {
        :operation => :add,
        :device => VIM.VirtualE1000(
          :key => -3,
          :deviceInfo => {
            :label => 'Network Adapter 1',
            :summary => 'VM Network'
          },
          :backing => VIM.VirtualEthernetCardNetworkBackingInfo(
            :deviceName => 'VM Network'
          ),
          :addressType => 'generated'
        )
      }
    ],
  }
  vmFolder.CreateVM_Task(:config => config,
                         :pool => opts[:pool],
                         :host => opts[:host]).wait_for_completion
end

#cur_auth_mgrObject



1
2
3
4
# File 'lib/rvc/modules/role.rb', line 1

def cur_auth_mgr
  conn = $shell.fs.cur._connection
  conn.serviceContent.authorizationManager
end

#debugObject



105
106
107
108
109
110
# File 'lib/rvc/modules/basic.rb', line 105

def debug
  debug = $shell.debug = !$shell.debug
  $shell.connections.each do |name,conn|
    conn.debug = debug
  end
end

#delete(name, opts) ⇒ Object



65
66
67
68
69
# File 'lib/rvc/modules/role.rb', line 65

def delete name, opts
  role = cur_auth_mgr.roleList.find { |x| x.name == name }
  err "no such role #{role_name.inspect}" unless role
  cur_auth_mgr.RemoveAuthorizationRole :roleId => role.roleId, :failIfUsed => opts[:force]
end

#deltaize_disks(vm) ⇒ Object



654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
# File 'lib/rvc/modules/vm.rb', line 654

def deltaize_disks vm
  real_disks = vm.config.hardware.device.grep(VIM::VirtualDisk).select { |x| x.backing.parent == nil }
  unless real_disks.empty?
    puts "Reconfiguring source VM to use delta disks..."
    deviceChange = []
    real_disks.each do |disk|
      deviceChange << { :operation => :remove, :device => disk }
      deviceChange << {
        :operation => :add,
        :fileOperation => :create,
        :device => disk.dup.tap { |x|
          x.backing = x.backing.dup
          x.backing.fileName = "[#{disk.backing.datastore.name}]"
          x.backing.parent = disk.backing
        }
      }
    end
    progress [vm.ReconfigVM_Task(:spec => { :deviceChange => deviceChange })]
  end
end

#describe(snapshot, description) ⇒ Object



48
49
50
# File 'lib/rvc/modules/snapshot.rb', line 48

def describe snapshot, description
  snapshot.find_tree.snapshot.RenameSnapshot :description => description
end

#destroy(objs) ⇒ Object



234
235
236
# File 'lib/rvc/modules/basic.rb', line 234

def destroy objs
  tasks objs, :Destroy
end

#devices(vm) ⇒ Object



362
363
364
365
366
367
368
369
# File 'lib/rvc/modules/vm.rb', line 362

def devices vm
  devs = vm.config.hardware.device
  devs.each do |dev|
    tags = []
    tags << (dev.connectable.connected ? :connected : :disconnected) if dev.props.member? :connectable
    puts "#{dev.deviceInfo.label} (#{dev.class}): #{dev.deviceInfo.summary}; #{tags * ' '}"
  end
end

#disconnect(connection) ⇒ Object



389
390
391
# File 'lib/rvc/modules/vm.rb', line 389

def disconnect vm, label
  change_device_connectivity vm, label, false
end

#download(file, local_path) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rvc/modules/vmrc.rb', line 119

def download url_str, dest
  puts "Downloading VMRC..."

  url = URI.parse(url_str)

  http = if ENV['http_proxy']
    proxy_uri = URI.parse(ENV['http_proxy'])
    proxy_user, proxy_pass = proxy_uri.userinfo.split(/:/) if proxy_uri.userinfo
    Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_user, proxy_pass)
  else
    Net::HTTP
  end

  begin
    File.open(dest, 'wb') do |io|
      res = http.start(url.host, url.port) do |http|
        http.get(url.path) do |segment|
          io.write segment
        end
      end
    end
  rescue Exception
    err "Error downloading VMRC: #{$!.class}: #{$!.message}"
  end
end

#edit(file) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rvc/modules/mark.rb', line 43

def edit key
  editor = ENV['VISUAL'] || ENV['EDITOR'] || 'vi'
  objs = $shell.session.get_mark(key) or err "no such mark #{key.inspect}"
  filename = File.join(Dir.tmpdir, "rvc.#{Time.now.to_i}.#{rand(65536)}")
  File.open(filename, 'w') { |io| objs.each { |obj| io.puts(obj.rvc_path_str) } }
  begin
    system("#{editor} #{filename}")
    new_paths = File.readlines(filename).map(&:chomp) rescue return
    new_objs = new_paths.map { |path| lookup(path) }.inject([], &:+)
    mark key, new_objs
  ensure
    File.unlink filename
  end
end

#enter_maintenance_mode(hosts, opts) ⇒ Object



81
82
83
# File 'lib/rvc/modules/host.rb', line 81

def enter_maintenance_mode hosts, opts
  tasks hosts, :EnterMaintenanceMode, :timeout => opts[:timeout]
end

#evacuate(src, dsts, opts) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rvc/modules/host.rb', line 39

def evacuate src, dsts, opts
  vim = src._connection
  vms = src.vm
  dst_hosts = dsts.map(&:host).flatten
  checks = ['cpu', 'software']

  dst_hosts.reject! { |host| host == src ||
                             host.runtime.connectionState != 'connected' ||
                             host.runtime.inMaintenanceMode }

  candidates = {}
  vms.each do |vm|
    required_datastores = vm.datastore
    result = vim.serviceInstance.QueryVMotionCompatibility(:vm => vm,
                                                           :host => dst_hosts,
                                                           :compatibility => checks)
    result.reject! { |x| x.compatibility != checks ||
                         x.host.datastore & required_datastores != required_datastores }
    candidates[vm] = result.map { |x| x.host }
  end

  if candidates.any? { |vm,hosts| hosts.empty? }
    puts "The following VMs have no compatible vMotion destination:"
    candidates.select { |vm,hosts| hosts.empty? }.each { |vm,hosts| puts " #{vm.name}" }
    return
  end

  tasks = candidates.map do |vm,hosts|
    host = hosts[rand(hosts.size)]
    vm.MigrateVM_Task(:host => host, :priority => :defaultPriority)
  end

  progress tasks
end

#events(obj, opts) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/rvc/modules/basic.rb', line 330

def events obj, opts
  err "'events' not supported at this level" unless obj.respond_to?(:_connection)
  manager = obj._connection.serviceContent.eventManager
  @event_details ||= Hash[manager.collect("description.eventInfo").first.collect { |d| [d.key, d] }]

  spec = VIM::EventFilterSpec(:entity => VIM::EventFilterSpecByEntity(:entity => obj, :recursion => "all"))

  collector = manager.CreateCollectorForEvents(:filter => spec)
  collector.SetCollectorPageSize(:maxCount => opts[:lines])
  collector.latestPage.reverse.each do |event|
    time = event.createdTime.localtime.strftime("%m/%d/%Y %I:%M %p")
    category = @event_details[event.class.to_s].category
    puts "[#{time}] [#{category}] #{event.fullFormattedMessage.strip}"
  end
ensure
  collector.DestroyCollector if collector
end

#exit_maintenance_mode(hosts, opts) ⇒ Object



92
93
94
# File 'lib/rvc/modules/host.rb', line 92

def exit_maintenance_mode hosts, opts
  tasks hosts, :ExitMaintenanceMode, :timeout => opts[:timeout]
end

#extraConfig(vm, regexes) ⇒ Object



426
427
428
# File 'lib/rvc/modules/vm.rb', line 426

def extraConfig vm, regexes
  _extraConfig(vm, *regexes.map { |x| /#{x}/ })
end

#extract(src, dst) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/rvc/modules/vmrc.rb', line 151

def extract src, dst
  puts "Installing VMRC..."
  FileUtils.mkdir_p dst
  Zip::ZipFile.open(src) do |zf|
    zf.each do |e|
      dst_filename = File.join(dst, e.name)
      case e.ftype
      when :file
        FileUtils.mkdir_p File.dirname(dst_filename)
        zf.extract e.name, dst_filename
        File.chmod(e.unix_perms, dst_filename) if e.unix_perms
      when :directory
        FileUtils.mkdir_p dst_filename
      else
        $stderr.puts "unknown file type #{e.ftype}"
      end
    end
  end
end

#find(ds, opts) ⇒ Object



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/rvc/modules/vm.rb', line 401

def find ds, opts
  folder = opts[:folder]
  rp = opts[:resource_pool] || opts[:folder]._connection.rootFolder.childEntity[0].hostFolder.childEntity[0].resourcePool

  paths = find_vmx_files(ds)
  if paths.empty?
    puts "no VMX files found"
    return
  end

  puts "Select a VMX file"
  path = menu(paths) or return

  folder.RegisterVM_Task(:path => path,
                         :asTemplate => false,
                         :pool => rp).wait_for_completion
end

#find_ancestor(klass) ⇒ Object



151
152
153
# File 'lib/rvc/modules/basic.rb', line 151

def find_ancestor klass
  $shell.fs.cur.rvc_path.map { |k,v| v }.reverse.find { |x| x.is_a? klass }
end

#find_local_vmrcObject



45
46
47
48
49
# File 'lib/rvc/modules/vmrc.rb', line 45

def find_local_vmrc
  return nil if VMRC_NAME.nil?
  path = File.join(Dir.tmpdir, VMRC_NAME, 'plugins', VMRC_BIN)
  File.exists?(path) && path
end

#find_vmrcObject



51
52
53
# File 'lib/rvc/modules/vmrc.rb', line 51

def find_vmrc
  find_local_vmrc || search_path('vmrc')
end

#find_vmx_files(ds) ⇒ Object



711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
# File 'lib/rvc/modules/vm.rb', line 711

def find_vmx_files ds
  datastorePath = "[#{ds.name}] /"
  searchSpec = {
    :details => { :fileOwner => false, :fileSize => false, :fileType => true, :modification => false  },
    :query => [
      VIM::VmConfigFileQuery()
    ]
  }
  task = ds.browser.SearchDatastoreSubFolders_Task(:datastorePath => datastorePath, :searchSpec => searchSpec)

  results = task.wait_for_completion

  files = []
  results.each do |result|
    result.file.each do |file|
      files << "#{result.folderPath}/#{file.path}"
    end
  end

  files
end

#get(objs) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/rvc/modules/role.rb', line 22

def get name
  role = cur_auth_mgr.roleList.find { |x| x.name == name }
  err "no such role #{role_name.inspect}" unless role
  puts "label: #{role.info.label}"
  puts "summary: #{role.info.summary}"
  puts "privileges: #{role.privilege.sort * ' '}"
end

#help(path) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/rvc/modules/basic.rb', line 44

def help path
  if mod = RVC::MODULES[path]
    opts = mod.instance_variable_get(:@opts)
    opts.each do |method_name,method_opts|
      parser = RVC::OptionParser.new method_name, &method_opts
      help_summary parser, path, method_name
    end
    return
  elsif tgt = RVC::ALIASES[path]
    fail unless tgt =~ /^(.+)\.(.+)$/
    opts_block = RVC::MODULES[$1].opts_for($2.to_sym)
    RVC::OptionParser.new(tgt, &opts_block).educate
    return
  elsif path =~ /^(.+)\.(.+)$/ and
        mod = RVC::MODULES[$1] and
        opts_block = mod.opts_for($2.to_sym)
    RVC::OptionParser.new(path, &opts_block).educate
    return
  end

  obj = lookup_single(path) if path

  if obj
    puts "Relevant commands for #{obj.class}:"
  else
    puts "All commands:"
  end

  MODULES.sort_by do |mod_name,mod|
    HELP_ORDER.index(mod_name) || HELP_ORDER.size
  end.each do |mod_name,mod|
    opts = mod.instance_variable_get(:@opts)
    opts.each do |method_name,method_opts|
      parser = RVC::OptionParser.new method_name, &method_opts
      next unless obj.nil? or parser.applicable.any? { |x| obj.is_a? x }
      help_summary parser, mod_name, method_name
    end
  end

  if not obj
    puts (<<-EOS)

To see detailed help for a command, use its --help option.
To show only commands relevant to a specific object, use "help /path/to/object".
    EOS
  end
end

#help_summary(parser, mod_name, method_name) ⇒ Object



92
93
94
95
96
# File 'lib/rvc/modules/basic.rb', line 92

def help_summary parser, mod_name, method_name
  aliases = ALIASES.select { |k,v| v == "#{mod_name}.#{method_name}" }.map(&:first)
  aliases_text = aliases.empty? ? '' : " (#{aliases*', '})"
  puts "#{mod_name}.#{method_name}#{aliases_text}: #{parser.summary?}" if parser.summary?
end

#http_path(dc_name, ds_name, path) ⇒ Object



163
164
165
# File 'lib/rvc/modules/datastore.rb', line 163

def http_path dc_name, ds_name, path
  "/folder/#{URI.escape path}?dcPath=#{URI.escape dc_name}&dsName=#{URI.escape ds_name}"
end

#info(obj) ⇒ Object



217
218
219
220
221
222
223
224
# File 'lib/rvc/modules/basic.rb', line 217

def info obj
  puts "path: #{obj.rvc_path_str}"
  if obj.respond_to? :display_info
    obj.display_info
  else
    puts "class: #{obj.class.name}"
  end
end

#insert_cdrom(vm, iso) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rvc/modules/vm.rb', line 220

def insert_cdrom vm, iso
  device = vm.config.hardware.device.grep(VIM::VirtualCdrom)[0]
  err "No virtual CDROM drive found" unless device

  device.backing = VIM.VirtualCdromIsoBackingInfo(:fileName => iso.datastore_path)

  spec = {
    :deviceChange => [
      {
        :operation => :edit,
        :device => device
      }
    ]
  }
  
  vm.ReconfigVM_Task(:spec => spec)
end

#installObject



111
112
113
114
115
116
117
# File 'lib/rvc/modules/vmrc.rb', line 111

def install
  zip_filename = File.join(Dir.tmpdir, VMRC_BASENAME)
  download VMRC_URL, zip_filename
  verify zip_filename, VMRC_SHA256
  extract zip_filename, File.join(Dir.tmpdir, VMRC_NAME)
  puts "VMRC was installed successfully."
end

#ip(vms) ⇒ Object



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/rvc/modules/vm.rb', line 514

def ip vms
  props = %w(summary.runtime.powerState summary.guest.ipAddress summary.config.annotation)
  connection = single_connection vms

  filters = vms.map do |vm|
    connection.propertyCollector.CreateFilter :spec => {
      :propSet => [{ :type => 'VirtualMachine', :all => false, :pathSet => props }],
      :objectSet => [{ :obj => vm }],
    }, :partialUpdates => false
  end

  ver = ''
  while not vms.empty?
    result = connection.propertyCollector.WaitForUpdates(:version => ver)
    ver = result.version

    vms.reject! do |vm|
      begin
        ip = vm_ip(vm)
        puts "#{vm.name}: #{ip}"
        true
      rescue UserError
        false
      end
    end
  end
ensure
  filters.each(&:DestroyPropertyFilter) if filters
end

#keychain_password(username, hostname) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rvc/modules/vim.rb', line 149

def keychain_password username , hostname
   return nil unless RbConfig::CONFIG['host_os'] =~ /^darwin10/

  begin
    require 'osx_keychain'
  rescue LoadError
    return nil
  end

  keychain = OSXKeychain.new
  return keychain["rvc", "#{username}@#{hostname}" ]

end

#kill(vms) ⇒ Object



323
324
325
326
327
# File 'lib/rvc/modules/vm.rb', line 323

def kill vms
  on_vms = vms.select { |x| x.summary.runtime.powerState == 'poweredOn' }
  off on_vms unless on_vms.empty?
  CMD.basic.destroy vms unless vms.empty?
end

#layout(vm) ⇒ Object



350
351
352
353
354
# File 'lib/rvc/modules/vm.rb', line 350

def layout vm
  vm.layoutEx.file.each do |f|
    puts "#{f.type}: #{f.name}"
  end
end

#listObject



63
64
65
# File 'lib/rvc/modules/mark.rb', line 63

def list
  $shell.session.marks.each { |x| puts x }
end

#ls(obj) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/rvc/modules/basic.rb', line 164

def ls obj
  children = obj.children
  name_map = children.invert
  children, fake_children = children.partition { |k,v| v.is_a? VIM::ManagedEntity }
  i = 0

  fake_children.each do |name,child|
    puts "#{i} #{name}#{child.ls_text(nil)}"
    child.rvc_link obj, name
    CMD.mark.mark i.to_s, [child]
    i += 1
  end

  return if children.empty?

  filterSpec = VIM.PropertyFilterSpec(:objectSet => [], :propSet => [])
  filteredTypes = Set.new

  children.each do |name,child|
    filterSpec.objectSet << { :obj => child }
    filteredTypes << child.class
  end

  filteredTypes.each do |x|
    filterSpec.propSet << {
      :type => x.wsdl_name,
      :pathSet => x.ls_properties+%w(name),
    }
  end

  connection = single_connection(children.map { |k,v| v })
  results = connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec])

  results.each do |r|
    name = name_map[r.obj]
    text = r.obj.ls_text(r) rescue " (error)"
    realname = r['name'] if name != r['name']
    puts "#{i} #{name}#{realname && " [#{realname}]"}#{text}"
    r.obj.rvc_link obj, name
    CMD.mark.mark i.to_s, [r.obj]
    i += 1
  end
end

#mark(key, objs) ⇒ Object



30
31
32
33
# File 'lib/rvc/modules/mark.rb', line 30

def mark key, objs
  err "invalid mark name" unless key =~ /^\w+$/
  $shell.session.set_mark key, objs
end

#migrate(vms, opts) ⇒ Object



612
613
614
615
616
# File 'lib/rvc/modules/vm.rb', line 612

def migrate vms, opts
  tasks vms, :MigrateVM, :pool => opts[:pool],
                         :host => opts[:host],
                         :priority => :defaultPriority
end

#mkdir(datastore_path) ⇒ Object

TODO dispatch to datastore.mkdir if path is in a datastore



316
317
318
319
# File 'lib/rvc/modules/basic.rb', line 316

def mkdir path
  parent = lookup_single! File.dirname(path), RbVmomi::VIM::Folder
  parent.CreateFolder(:name => File.basename(path))
end

#modify_cpu(vm, opts) ⇒ Object



693
694
695
696
# File 'lib/rvc/modules/vm.rb', line 693

def modify_cpu vm, opts
  spec = { :numCPUs => opts[:num] }
  tasks [vm], :ReconfigVM, :spec => spec
end

#modify_memory(vm, opts) ⇒ Object



705
706
707
708
# File 'lib/rvc/modules/vm.rb', line 705

def modify_memory vm, opts
  spec = { :memoryMB => opts[:size] }
  tasks [vm], :ReconfigVM, :spec => spec
end

#mv(objs) ⇒ Object



274
275
276
277
278
# File 'lib/rvc/modules/basic.rb', line 274

def mv objs
  err "Destination entity missing" unless objs.size > 1
  dst = objs.pop
  progress [dst.MoveIntoFolder_Task(:list => objs)]
end

#off(vm) ⇒ Object



40
41
42
# File 'lib/rvc/modules/vm.rb', line 40

def off vms
  tasks vms, :PowerOffVM
end

#on(vms) ⇒ Object



28
29
30
# File 'lib/rvc/modules/vm.rb', line 28

def on vms
  tasks vms, :PowerOnVM
end

#permissions(name) ⇒ Object



36
37
38
39
40
41
42
43
44
45
# File 'lib/rvc/modules/role.rb', line 36

def permissions name
  role = cur_auth_mgr.roleList.find { |x| x.name == name }
  err "no such role #{role_name.inspect}" unless role
  cur_auth_mgr.RetrieveRolePermissions(:roleId => role.roleId).each do |perm|
    flags = []
    flags << 'group' if perm[:group]
    flags << 'propagate' if perm[:propagate]
    puts " #{perm[:principal]}#{flags.empty? ? '' : " (#{flags * ', '})"}: #{perm.entity.name}"
  end
end

#ping(vm) ⇒ Object



503
504
505
506
# File 'lib/rvc/modules/vm.rb', line 503

def ping vm
  ip = vm_ip vm
  system_fg "ping #{Shellwords.escape ip}"
end

#prompt_passwordObject



145
146
147
# File 'lib/rvc/modules/vim.rb', line 145

def prompt_password
  ask("password: ") { |q| q.echo = false }
end

#quitObject



121
122
123
# File 'lib/rvc/modules/basic.rb', line 121

def quit
  exit
end

#reachable_ip(host) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/rvc/modules/vnc.rb', line 70

def reachable_ip host
  ips = host.config.network.vnic.map { |x| x.spec.ip.ipAddress } # TODO optimize
  ips.find do |x|
    begin
      Timeout.timeout(1) { TCPSocket.new(x, 443).close; true }
    rescue
      false
    end
  end or err("could not find IP for server #{host.name}")
end

#realdisksize(size) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/rvc/modules/vm.rb', line 119

def realdisksize( size )
  size.downcase!
  if size =~ /([0-9][0-9,]*)([mgt])?/i
    size = $1.delete(',').to_i
    unit = $2

    case unit
    when 'm'
      return size * 1024
    when 'g'
      return size * ( 1024 ** 2 )
    when 't'
      return size * ( 1024 ** 3 )
    when nil
      return size
    else
      err "Unknown size modifer of '#{unit}'"
    end
  else
    err "Problem with #{size}"
  end
end

#reboot(hosts, opts) ⇒ Object



27
28
29
# File 'lib/rvc/modules/host.rb', line 27

def reboot hosts, opts
  tasks hosts, :RebootHost, :force => opts[:force]
end

#reboot_guest(vms) ⇒ Object



96
97
98
# File 'lib/rvc/modules/vm.rb', line 96

def reboot_guest vms
  vms.each(&:RebootGuest)
end

#reconnect(hosts, opts) ⇒ Object



114
115
116
117
118
119
120
121
# File 'lib/rvc/modules/host.rb', line 114

def reconnect hosts, opts
  spec = {
    :force => false,
    :userName => opts[:username],
    :password => opts[:password],
  }
  tasks hosts, :ReconnectHost
end

#register(vmx_file, opts) ⇒ Object



245
246
247
248
249
250
# File 'lib/rvc/modules/vm.rb', line 245

def register vmx_file, opts
  rp = opts[:resource_pool] || opts[:folder]._connection.rootFolder.childEntity[0].hostFolder.childEntity[0].resourcePool
  vm = opts[:folder].RegisterVM_Task(:path => vmx_file.datastore_path,
                                     :asTemplate => false,
                                     :pool => rp).wait_for_completion
end

#reloadObject



132
133
134
# File 'lib/rvc/modules/basic.rb', line 132

def reload
  RVC.reload_modules
end

#reload_entity(objs) ⇒ Object



246
247
248
# File 'lib/rvc/modules/basic.rb', line 246

def reload_entity objs
  objs.each(&:Reload)
end

#remove(objs, opts) ⇒ Object



59
60
61
# File 'lib/rvc/modules/snapshot.rb', line 59

def remove snapshot, opts
  tasks [snapshot.find_tree.snapshot], :RemoveSnapshot, :removeChildren => opts[:remove_children]
end

#remove_device(vm, label) ⇒ Object



593
594
595
596
597
598
599
600
601
602
# File 'lib/rvc/modules/vm.rb', line 593

def remove_device vm, label
  dev = vm.config.hardware.device.find { |x| x.deviceInfo.label == label }
  err "no such device" unless dev
  spec = {
    :deviceChange => [
      { :operation => :remove, :device => dev },
    ]
  }
  vm.ReconfigVM_Task(:spec => spec).wait_for_completion
end

#remove_privilege(name, privileges) ⇒ Object



109
110
111
112
113
114
115
116
# File 'lib/rvc/modules/role.rb', line 109

def remove_privilege name, privileges
  role = cur_auth_mgr.roleList.find { |x| x.name == name }
  err "no such role #{name.inspect}" unless role
  cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
                                       :newName => role.name,
                                       :privIds => (role.privilege - privileges)

end

#rename(snapshot, name) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/rvc/modules/role.rb', line 78

def rename old, new
  role = cur_auth_mgr.roleList.find { |x| x.name == old }
  err "no such role #{old.inspect}" unless role
  cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
                                       :newName => new,
                                       :privIds => role.privilege
end

#reset(vms) ⇒ Object



53
54
55
# File 'lib/rvc/modules/vm.rb', line 53

def reset vms
  tasks vms, :ResetVM
end

#revert(arg) ⇒ Object



22
23
24
25
26
27
28
# File 'lib/rvc/modules/snapshot.rb', line 22

def revert arg
  if arg.is_a? VIM::VirtualMachine
    tasks [arg], :RevertToCurrentSnapshot
  else
    tasks [arg.find_tree.snapshot], :RevertToSnapshot
  end
end

#rvc(vm, opts) ⇒ Object



485
486
487
488
489
490
491
492
493
# File 'lib/rvc/modules/vm.rb', line 485

def rvc vm, opts
  ip = vm_ip vm

  env = Hash[%w(RBVMOMI_PASSWORD RBVMOMI_HOST RBVMOMI_USER RBVMOMI_SSL RBVMOMI_PORT
                RBVMOMI_FOLDER RBVMOMI_DATASTORE RBVMOMI_PATH RBVMOMI_DATACENTER
                RBVMOMI_COMPUTER).map { |k| [k,nil] }]
  cmd = "rvc #{opts[:user] && Shellwords.escape("#{opts[:user]}@")}#{Shellwords.escape ip}"
  system_fg(cmd, env)
end

#save_keychain_password(username, password, hostname) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/rvc/modules/vim.rb', line 163

def save_keychain_password username , password , hostname
  # only works for OSX at the minute.
  return false unless RbConfig::CONFIG['host_os'] =~ /^darwin10/

  # check we already managed to load that gem.
  if defined? OSXKeychain::VERSION

    if agree("Save password for connection (y/n)? ", true)
      keychain = OSXKeychain.new

      # update the keychain, unless it's already set to that.
      keychain.set("rvc", "#{username}@#{hostname}" , password ) unless 
        keychain["rvc", "#{username}@#{hostname}" ] == password
    end
  else
    return false
  end
end

#set(objs, opts) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/rvc/modules/permissions.rb', line 32

def set objs, opts
  conn = single_connection objs
  authMgr = conn.serviceContent.authorizationManager
  role = authMgr.roleList.find { |x| x.name == opts[:role] }
  err "no such role #{role.inspect}" unless role
  perm = { :roleId => role.roleId,
           :principal => opts[:principal],
           :group => opts[:group],
           :propagate => opts[:propagate] }
  objs.each do |obj|
    authMgr.SetEntityPermissions(:entity => obj, :permission => [perm])
  end
end

#setExtraConfig(vm, pairs) ⇒ Object



437
438
439
440
# File 'lib/rvc/modules/vm.rb', line 437

def setExtraConfig vm, pairs
  h = Hash[pairs.map { |x| x.split('=', 2).tap { |a| a << '' if a.size == 1 } }]
  _setExtraConfig vm, h
end

#shares_from_string(str) ⇒ Object



35
36
37
38
39
40
41
42
43
44
# File 'lib/rvc/modules/resource_pool.rb', line 35

def shares_from_string str
  case str
  when 'normal', 'low', 'high'
    { :level => str, :shares => 0 }
  when /^\d+$/
    { :level => 'custom', :shares => str.to_i }
  else
    err "Invalid shares argument #{str.inspect}"
  end
end

#show(objs) ⇒ Object



259
260
261
262
263
# File 'lib/rvc/modules/basic.rb', line 259

def show objs
  objs.each do |obj|
    puts "#{obj.rvc_path_str}: #{obj.class}"
  end
end

#shutdown_guest(vms) ⇒ Object



76
77
78
# File 'lib/rvc/modules/vm.rb', line 76

def shutdown_guest vms
  vms.each(&:ShutdownGuest)
end

#ssh(vm, cmd, opts) ⇒ Object



469
470
471
472
473
474
# File 'lib/rvc/modules/vm.rb', line 469

def ssh vm, cmd, opts
  ip = vm_ip vm
  cmd_arg = cmd ? Shellwords.escape(cmd) : ""
  ssh_cmd = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l #{Shellwords.escape opts[:login]} #{Shellwords.escape ip} #{cmd_arg}"
  system_fg(ssh_cmd)
end

#standby_guest(vms) ⇒ Object



86
87
88
# File 'lib/rvc/modules/vm.rb', line 86

def standby_guest vms
  vms.each(&:StandbyGuest)
end

#stats_time(secs) ⇒ Object

see vSphere Client: Administration -> vCenter Server Settings -> Statistics -> Statistics Intervals



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/rvc/modules/statsinterval.rb', line 23

def stats_time secs
  length = secs / 60

  [[60, "Minutes"], [24, "Hours"], [7, "Days"], [4, "Weeks"], [12, "Months"]].each do |div, name|
    if length < div
      return "#{length} #{name}"
    end
    length = length / div
  end

  return "#{length} Years"
end

#suspend(vms) ⇒ Object



66
67
68
# File 'lib/rvc/modules/vm.rb', line 66

def suspend vms
  tasks vms, :SuspendVM
end

#tasksObject



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/rvc/modules/vim.rb', line 216

def tasks
  conn = single_connection [$shell.fs.cur]

  begin
    view = conn.serviceContent.viewManager.CreateListView

    collector = conn.serviceContent.taskManager.CreateCollectorForTasks(:filter => {
      :time => {
        :beginTime => conn.serviceInstance.CurrentTime.to_datetime, # XXX
        :timeType => :queuedTime
      }
    })
    collector.SetCollectorPageSize :maxCount => 1

    filter_spec = {
      :objectSet => [
        {
          :obj => view,
          :skip => true,
          :selectSet => [
            VIM::TraversalSpec(:path => 'view', :type => view.class.wsdl_name)
          ]
        },
        { :obj => collector },
      ],
      :propSet => [
        { :type => 'Task', :pathSet => %w(info.state) },
        { :type => 'TaskHistoryCollector', :pathSet => %w(latestPage) },
      ]
    }
    filter = conn.propertyCollector.CreateFilter(:partialUpdates => false, :spec => filter_spec)

    ver = ''
    loop do
      result = conn.propertyCollector.WaitForUpdates(:version => ver)
      ver = result.version
      result.filterSet[0].objectSet.each do |r|
        remove = []
        case r.obj
        when VIM::TaskHistoryCollector
          infos = collector.ReadNextTasks :maxCount => 100
          view.ModifyListView :add => infos.map(&:task)
        when VIM::Task
          puts "#{Time.now} #{r.obj.info.name} #{r.obj.info.entityName} #{r['info.state']}" unless r['info.state'] == nil
          remove << r.obj if %w(error success).member? r['info.state']
        end
        view.ModifyListView :remove => remove unless remove.empty?
      end
    end
  rescue Interrupt
  ensure
    filter.DestroyPropertyFilter if filter
    collector.DestroyCollector if collector
    view.DestroyView if view
  end
end

#type(name) ⇒ Object



28
29
30
31
32
# File 'lib/rvc/modules/basic.rb', line 28

def type name
  klass = RbVmomi::VIM.type(name) rescue err("#{name.inspect} is not a VMODL type.")
  $shell.introspect_class klass
  nil
end

#unregister(vm) ⇒ Object



310
311
312
# File 'lib/rvc/modules/vm.rb', line 310

def unregister vm
  vm.UnregisterVM
end

#unused_vnc_port(ip) ⇒ Object



81
82
83
84
85
86
87
88
# File 'lib/rvc/modules/vnc.rb', line 81

def unused_vnc_port ip
  10.times do
    port = 5901 + rand(64)
    unused = (TCPSocket.connect(ip, port).close rescue true)
    return port if unused
  end
  err "no unused port found"
end

#update(name, opts) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rvc/modules/resource_pool.rb', line 79

def update pool, opts
  spec = {
    :cpuAllocation => {
      :limit => opts[:cpu_limit],
      :reservation => opts[:cpu_reservation],
      :expandableReservation => opts[:cpu_expandable],
      :shares => shares_from_string(opts[:cpu_shares]),
    },
    :memoryAllocation => {
      :limit => opts[:mem_limit],
      :reservation => opts[:mem_reservation],
      :expandableReservation => opts[:mem_expandable],
      :shares => shares_from_string(opts[:mem_shares]),
    },
  }
  pool.UpdateConfig(:name => opts[:name], :spec => spec)
end

#upload(local_path, dest) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rvc/modules/datastore.rb', line 65

def upload local_path, dest
  dir, datastore_filename = *dest
  err "local file does not exist" unless File.exists? local_path
  real_datastore_path = "#{dir.path}/#{datastore_filename}"

  main_http = dir.datastore._connection.http
  http = Net::HTTP.new(main_http.address, main_http.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  #http.set_debug_output $stderr
  http.start
  err "certificate mismatch" unless main_http.peer_cert.to_der == http.peer_cert.to_der

  File.open(local_path, 'rb') do |io|
    stream = ProgressStream.new(io, io.stat.size) do |s|
      $stdout.write "\e[0G\e[Kuploading #{s.count}/#{s.len} bytes (#{(s.count*100)/s.len}%)"
      $stdout.flush
    end

    headers = {
      'cookie' => dir.datastore._connection.cookie,
      'content-length' => io.stat.size.to_s,
      'Content-Type' => 'application/octet-stream',
    }
    path = http_path dir.datastore.send(:datacenter).name, dir.datastore.name, real_datastore_path
    request = Net::HTTP::Put.new path, headers
    request.body_stream = stream
    res = http.request(request)
    $stdout.puts
    case res
    when Net::HTTPOK
    else
      err "upload failed: #{res.message}"
    end
  end
end

#verify(filename, expected_hash) ⇒ Object



145
146
147
148
149
# File 'lib/rvc/modules/vmrc.rb', line 145

def verify filename, expected_hash
  puts "Checking integrity..."
  hexdigest = Digest::SHA256.file(filename).hexdigest
  err "Hash mismatch" if hexdigest != VMRC_SHA256
end

#view(vms, opts) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rvc/modules/vnc.rb', line 31

def view vm
  ip = reachable_ip vm.runtime.host
  extraConfig = vm.config.extraConfig
  already_enabled = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.enabled' && x.value.downcase == 'true' }
  if already_enabled
    puts "VNC already enabled"
    port = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.port' }.value
    password = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.password' }.value
  else
    port = unused_vnc_port ip
    password = vnc_password
    vm.ReconfigVM_Task(:spec => {
      :extraConfig => [
        { :key => 'RemoteDisplay.vnc.enabled', :value => 'true' },
        { :key => 'RemoteDisplay.vnc.password', :value => password },
        { :key => 'RemoteDisplay.vnc.port', :value => port.to_s }
      ]
    }).wait_for_completion
  end
  vnc_client ip, port, password
end

#vm_ip(vm) ⇒ Object



745
746
747
748
749
750
751
752
753
754
755
756
757
# File 'lib/rvc/modules/vm.rb', line 745

def vm_ip vm
  summary = vm.summary

  err "VM is not powered on" unless summary.runtime.powerState == 'poweredOn'

  ip = if summary.guest.ipAddress and summary.guest.ipAddress != '127.0.0.1'
    summary.guest.ipAddress
  elsif note = YAML.load(summary.config.annotation) and note.is_a? Hash and note.member? 'ip'
    note['ip']
  else
    err "no IP known for this VM"
  end
end

#vnc_client(ip, port, password) ⇒ Object

Override this to spawn a VNC client differently



98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/rvc/modules/vnc.rb', line 98

def vnc_client ip, port, password
  if VNC
    fork do
      $stderr.reopen("#{ENV['HOME']||'.'}/.rvc-vmrc.log", "w")
      Process.setpgrp
      exec VNC, "#{ip}:#{port}"
    end
    puts "spawning #{VNC}"
    puts "#{ip}:#{port} password: #{password}"
  else
    puts "no VNC client configured"
    puts "#{ip}:#{port} password: #{password}"
  end
end

#vnc_passwordObject

Override this if you don’t want a random password



91
92
93
94
95
# File 'lib/rvc/modules/vnc.rb', line 91

def vnc_password
  n = 8
  chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
  (0...n).map { chars[rand(chars.length)].chr }.join
end